Path: news.duke.edu!newsgate.duke.edu!nntp-out.monmouth.com!newspeer.monmouth.com!news.maxwell.syr.edu!sn-xit-03!sn-xit-02!supernews.com!postnews1.google.com!not-for-mail
From: simonb@caldera.com (Simon Baldwin)
Newsgroups: rec.arts.int-fiction
Subject: Kwest 0.9 patch, and build hints
Date: 5 Sep 2001 09:13:23 -0700
Organization: http://groups.google.com/
Lines: 187
Message-ID: <983626bd.0109050813.382b4d7a@posting.google.com>
NNTP-Posting-Host: 132.147.67.17
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: posting.google.com 999706404 12615 127.0.0.1 (5 Sep 2001 16:13:24 GMT)
X-Complaints-To: groups-abuse@google.com
NNTP-Posting-Date: 5 Sep 2001 16:13:24 GMT
Xref: news.duke.edu rec.arts.int-fiction:92198

I recently picked up the Kwest KDE Zip interpreter from Peter Bienstman
<peter.bienstman@rug.ac.be>, and while it does provide a nice interface
for running Inform and Infocom stories, I had a few problems building
it, and when built, found that it crashed in a couple of nasty ways on
my home system (RedHat 7.1, with Qt 2.3.0).

In particular, I ran into the following two problems, which I patched
around in the source:

 o Immediate crash on pressing any "non-character" key, for example Shift
   or Ctrl (it's all okay if I just keep my fingers away from "modifiers",
   though!).

 o General crash on exiting the program through the Quit menu item.

The attached patch represents my hasty and not-too-clean fixes for these
two problems, which should be useful for anyone else running into these
problems and wanting to run Kwest.  Once patched, the program behaves in
a more stable fashion.

Here is the script I used to patch and build Kwest.  It assumes that the
distribution file kwest-0.9.gz is in /tmp, and also that the attached
patch text is stored as /tmp/kwest-0.9.patch:

  mkdir /tmp/BUILD
  cd /tmp/BUILD
  gunzip -c </tmp/kwest-0.9.tar.gz | tar xvf -
  cd kwest-0.9
  patch -p0 </tmp/kwest-0.9.patch
  make -f admin/Makefile.common cvs
  MCOPIDL="/bin/true" ./configure
  make all

The MCOPIDL="/bin/true" was necessary on my RedHat 7.1 system, since I
found no mcopidl program present on it.  It may not be necessary for all
Linux distributions (for example, Caldera), but since Kwest doesn't seem
to use it, it doesn't matter one way or the other.

Once built, kwest can be run without actually installing, by

  cd /tmp/BUILD/kwest-0.9/kwest
  ./kwest

Although this won't find all of the icon pixmaps (notably, save, restore,
and restart will be "?" icons), the interface should come up, and run
stories.

There are probably some lurking problems still.  I'm not at all sure my
patches are watertight, and I found the program had a tendency to run
away uncontrolled when given games that do a lot of timed inputs (zball
seemed unstable, but zlife behaved mostly okay).

All of the above may seem like a lot of work.  However, Kwest has the
type of interface that I've wanted for a long while now, so I think it's
well worth the effort.  Peter's program was a nice find for me; I had
resigned myself to eventually writing something that looked a lot like
this, so I'm very pleased to have found it.

I've mailed Peter with these patches, but in the meantime, I hope this
information proves useful.

Simon

------------------------------------------------------------------------
Simon Baldwin                   | simonb@caldera.COM          (internet)
Kernel Personality Products     | (+1) 831 427-7503              (phone)
Caldera, Santa Cruz, CA 95060   | http://www.geocities.com/simon_baldwin
------------------------------------------------------------------------

Save as /tmp/kwest-0.9.patch:

--- kwest_old/kwestview.cpp     Sat Aug 11 07:22:12 2001
+++ kwest/kwestview.cpp Wed Sep  5 07:58:36 2001
@@ -316,12 +316,30 @@

 unsigned char KwestView::readChar(int timeout)
 {
-  if (timeout && !timer.isActive())
+  /* The original code read as follows:
+   *
+   *  if (timeout && !timer.isActive())
+   *    timer.start(timeout, true);
+   *
+   *  while (!event)
+   *    kapp->processOneEvent();
+   *
+   * This causes problems where a timeout read is called, followed very
+   * shortly by a non-timeout read.  The timeout is still active from
+   * the first, and since the second is not expecting to ever receive
+   * timeout, the VM is prone to crashing or hanging should the timeout
+   * be received.  Also, I'm not sure that the timeout should not be
+   * reset on every timed read, not just if inactive...
+   */
+  if (timeout)                         // Always start timer on timed read
     timer.start(timeout, true);

-  while (!event)
+  while (!event)                       // Wait for any notable event
     kapp->processOneEvent();

+  if (timeout)                         // Ensure timeout stopped
+    timer.stop();
+
   if (!forcedInput.isEmpty())
     return ZC_RETURN; // let readLine deal with it.

@@ -769,6 +787,17 @@
     case Qt::Key_F11:       key = ZC_FKEY_MIN + 10; return;
     case Qt::Key_F12:       key = ZC_FKEY_MIN + 11; return;
   }
+
+  /* Empirically, a QKeyEvent's ascii() method returns 0 if the key pressed
+     has no ASCII associated with it, for example, Shift.  Unfortunately,
+     this matches ZC_TIME_OUT, and if the call to clients did not specify
+     a timeout, then receiving ZC_TIME_OUT back confuses them, and generally
+     crashes or hangs the virtual machine.  So... we'll forget all about
+     this event if we get to here, by simply resetting event back to None.
+     The readChar() loop "while (!event)..." will therefore not terminate
+     on such QKeyEvents. */
+  if (e->ascii() == '\0')
+    event = None;
 }


--- kwest_old/kwest.cpp Sat Aug 11 07:22:23 2001
+++ kwest/kwest.cpp     Wed Sep  5 07:37:56 2001
@@ -30,6 +30,8 @@
 #include <kstdaction.h>
 #include <kfontdialog.h>
 #include <kcolordialog.h>
+#include <setjmp.h>            // A nasty shortcut to return from main
+extern "C" jmp_buf exit_jump;  // ----------------''------------------

 // application specific includes
 #include "kwest.h"
@@ -498,9 +500,22 @@
   setStatusMsg(i18n("Exiting..."));

   saveGeneralOptions();
-  close();

-  setStatusMsg(i18n("Ready."));
+  /* Close will refuse to comply if the user opts to cancel the operation.
+     In this case, reset the status message.  Otherwise, since the close
+     will have destroyed the complete widget set, we cannot, and dare not,
+     continue to unwind stack frames, so we'll longjmp() instead to the
+     return point of main().  This is not at all nice, but since we are
+     exiting anyway, it does the job for now.  Not doing this leads to
+     crashes and X windows errors in trying to manipulate now closed
+     widgets. */
+  if (close())
+    longjmp(exit_jump, 1);
+
+  if (frotzRunning)
+    setStatusMsg(i18n("Running story."));
+  else
+    setStatusMsg(i18n("Ready."));
 }


--- kwest_old/main.cpp  Sat Aug 11 07:53:21 2001
+++ kwest/main.cpp      Wed Sep  5 07:37:56 2001
@@ -18,6 +18,8 @@
 #include <kcmdlineargs.h>
 #include <kaboutdata.h>
 #include <klocale.h>
+#include <setjmp.h>            // A nasty shortcut to return from main
+jmp_buf exit_jump;             // ----------------''------------------

 #include "kwest.h"

@@ -70,5 +72,15 @@
       return 0;
   }

+  /* On selecting the Quit button of the interface, all of the widgets are
+     destroyed before stack frames can be unwound.  Normally, under these
+     circumstances, an application would simply call exit().  However, this
+     is the world of KDE/Qt, and such things are frowned upon.  So, as an
+     expedient fix, this module offers a global setjmp/longjmp buffer,
+     exit_jump.  A longjmp to exit_jump forces an immediate return from
+     main() with return code 0. */
+  if (setjmp(exit_jump) != 0)
+    return 0;
+
   return app.exec();
 }
