[plt-scheme] Extensibility of MrEd

From: Ben Goetter (goetter at mazama.net)
Date: Mon Sep 29 21:02:17 EDT 2008

Matthew Flatt wrote:
> At the Scheme level, there's a `get-eventspace' method on
> `top-level-window<%>', so you should have access to the relevant
> eventspace whenever you have access to a frame's HWND.
>
> Meanwhile, `mred/private/kernel' exports `middle-queue-key'. Its value
> is recognized specially by `queue-callback' when supplied as the
> "boolean" second argument, and it causes a callback to be added with
> the same priority as a GUI event. (Of course, the module
> `mred/private/kernel' is meant to be private to MrEd, but if you have
> enough privileges to load an extension, then it's probably fine also to
> access that module.)
>   
Following this advice and also looting the contents of 
mred/private/helper, I came up with

(require mred (prefix-in wx: mred/private/kernel)
(define (queue-window-callback w cb)
 (parameterize ((current-eventspace (send (send w get-top-level-window) 
get-eventspace)))
  (queue-callback cb wx:middle-queue-key)))

which seems to work when called like so

(define (foo-callback hwnd x y)
 (let ((w0 (assq hwnd *window-set*))) ;; alist of hwnd, canvas% pairs
  (unless w0 (error ...))
  (let ((w (cdr w0)))
    (queue-window-callback w (lambda () (send w on-foo (list '(foo) x 
y))))))
  #t)

This foo-callback proc is passed as a (_fun _uint _uint _uint -> _bool) 
FFI callback to a stub C DLL which does little more than subclass a 
canvas with

WNDPROC OldWndProc = 0; // set to the original wndproc of the canvas%
typedef BOOL (*SCHEME_CALLBACK)(HWND, UINT, UINT);
SCHEME_CALLBACK Dispatch = 0; // set via FFI to foo-callback
LRESULT CALLBACK NewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM 
lParam)
{ if (uMsg == WM_RBUTTONDOWN) // easy enough to catch
  { Dispatch(hwnd, LOWORD(lParam), HIWORD(lParam)); return 0; }
  return CallWindowProc(OldWndProc, hwnd, uMsg, wParam, lParam); }

This all works just fine under mred.exe. Continuing with this simple 
example, a canvas in my application window is subclassed to catch right 
mouse downs, and does some boring stuff when it's dispatched an on-foo 
with the coordinates of that right mouse down.  No big deal.  Run under 
the drscheme debugger, however, my program faults frequently in 
MrEdDispatchEvent after catching a few right-mouse-downs, the debugger 
displaying a lot of suspiciously zeroed registers.  You did warn me once

> Beware that there's a bad interaction between PLT Scheme threads and
> Windows: a Scheme-level thread swap cannot be allowed while handling a
> Windows message. The solution is usually to handle a message by just
> queuing a callback in the eventspace, and then return to Windows
> immediately; that works when you don't need to compute any particular
> return value for the message.
>   
Am I doing something here that would trigger a thread swap under the 
debugger?  Perhaps the transition through the callback into Scheme?  Or 
is my implementation of queue-window-callback toxic?

Thanks,
Ben



Posted on the users mailing list.