[Openmcl-devel] Displaying subviews under MS-Windows

Gary Byers gb at clozure.com
Tue Jul 26 21:47:54 PDT 2011


One rule of thumb - not always true but often largely so - is that a
lot of UI things in Cocoa work better (and in some cases only work) if
they're done on the main thread, where the main thread is basically
running an event loop (and application-specific UI things typically
happen in response to those events and possibly cause further events
to be generated.)  There are some explicit exceptions to this (there's
an example on the CCL Wiki that shows how to safely draw from other
threads) and the example you're following just makes some calls from
the listener and that seems to work fine, at least on MacOS.  In
Cocotron on Windows ... well, IDE windows generally have no problems
displaying their subviews (that's an exaggeration, Cocotron is a work
in progress, but I think it's true that they don't exhibit the problems
that you're seeing), but all of the code that creates those windows and
their view hierarchies runs on the event thread.

At a very low level, the OSX and Windows event models are different: in
OSX, the initial thread in the process is the only one that can receive
low-level events from the window server; IIRC, in Windows, window-specific
messages/events are sent to the message port of the thread that created
the window.  If I'm remembering that correctly, then the Windows window
server would send messages to the listener thread's message port and would
likely be wondering why the listener thread isn't responding to them; when
I tried your example when you sent an earlier variant of this message, that
certainly looked like what was happening.

If that's indeed what the problem is, it's unfortunate: it's be nice to
be able to just create UI objects from the listener and to always get away
with it, and you can get away with more of that on OSX than you apparently
can on Windows.

The simplest way to force code to run on the initial/main thread is to
just use PROCESS-INTERRUPT:

? (process-interrupt ccl::*initial-process* (lambda ()
                                              (create-a-window)
                                              (add-a-subview)
                                              (display-that-window)))

All that that really guarantees is that at some point ("soon") after it's
called, the target thread/process will execute the lambda and ordinarily
return to do whatever it was doing when it received the interrupt.  If you
just want to create a window and don't really care whether the listener prints
a prompt before/after/around the same time as the window appears, that may
be adequate.

Another way (in the IDE) to force the event thread to do something is to
use GUI:EXECUTE-IN-GUI.  In the simplest form, that's just:

? (gui:execute-in-gui (lambda () ...))

Unlike PROCESS-INTERRUPT, that'll wait until the event thread finishes executing
the function and will return whatever the function returns.

I think that it's fairly likely that this (different thread models imposing
different constraints on what you can do in what thread) is a likely and
consistent explanation.  As I said, that'd also be unfortunate, because it's
a nice lispy thing to do to just type things into the REPL and watch their
effects.  We've never been able to guarantee that arbitrary things would just
work on MacOS and it seems very likely that Windows would be more restrictive.
We've sometimes thought about offering a modified REPL (something like "read,
execute-in-gui, print") to address this, but I don't think that we've ever
done that.

On Tue, 26 Jul 2011, Greg Bennett wrote:

> Good morning from Greg Bennett
> I am trying to understand the relationship between the handling of subviews
> and
> the display of their host windows/views under MS-Windows.
> 
> What follows is a blow-by-blow account of my attempts to follow two of the
> supplied examples,
> which I realise are directed to those using Mac OS rather than Windows.
> 
> If I this is not the right place for such posts, or if I have been too
> verbose, please let me know.
> Perhaps there is documentation about what I observe. If so, I should be
> delighted to receive a
> pointer to it.
> 
> I have used ;-> to show lines returned by CCL
> 
> Thanks for any and all advice and comment
> Cheers /Greg Bennett
> 
> ;; In Clozure CL Version 1.7-dev-r14869? (WindowsX8632)!
> ;; Running under Windows7 Pro 64bit
> ;; Using 32bit executables and listener
> 
> ;; Initially, code from ccl/examples/cocoa/ui-elements//HOWTO.html
> (in-package :ccl)
> ;->#<Package "CCL">
> 
> ;; First sequence
> ;; (a) allocate my-window1
> ;; (b) equip it with attributes
> ;; (c) show it
> ;; (d) allocate my-button
> ;; (e) equip it too
> ;; (f) add my-button as a subview to my-window1
> ;; (g) add a label to my-button
> 
> (setf my-window1 (#/alloc (@class ns-window)))
> ;->#<NS-WINDOW [uninitialized] (#x3591640)>
> (ns:with-ns-rect (r 100 100 400 300)
> ? (#/initWithContentRect:styleMask:backing:defer:
> ?? my-window1
> ?? r
> ?? (logior? #$NSTitledWindowMask
> ??????????? #$NSClosableWindowMask ?
> ??????????? #$NSMiniaturizableWindowMask
> ??????????? #$NSResizableWindowMask)
> ?? #$NSBackingStoreBuffered
> ?? #$NO))
> ;->;Compiler warnings :
> ;->;?? In an anonymous lambda form at position 93: Undeclared free variable
> MY-WINDOW1
> ;->#<NS-WINDOW <NSWindow 0x03591640> (#x3591640)> ?
> (#/makeKeyAndOrderFront: my-window1 nil)
> ;->NIL
> 
> ;;; .. and there is the window
> 
> (setf my-button (#/alloc ns:ns-button))
> ;->#<NS-BUTTON [uninitialized] (#x36981D0)>
> (ns:with-ns-rect (frame 10 10 72 32)
> ? (#/initWithFrame: my-button frame)
> ? (#/setButtonType: my-button #$NSMomentaryPushInButton)
> ? (#/setImagePosition: my-button #$NSNoImage)
> ? (#/setBezelStyle: my-button #$NSRoundedBezelStyle))
> ;->;Compiler warnings :
> ;->;?? In an anonymous lambda form at position 183: Undeclared free variable
> MY-BUTTON (4 references)
> ;->NIL ?
> (#/addSubview: (#/contentView my-window1) my-button)
> ;->NIL
> 
> ;;; and my-window1 is unchanged in appearance
> 
> ;;; Check on the subviews of my-window1
> 
> (#/subviews (#/contentView my-window1))
> ;->#<NS-MUTABLE-ARRAY (
> ;-> "<NSButton[0x36981d0] frame: {{10, 10}, {72, 32}}>"
> ;-> ) (#x35F19E8)>
> 
> ;;; so the data structure behind my-window1 seems to know about the button
> 
> (let ((label (%make-nsstring "Hello!")))
> ? (#/setTitle: my-button label)
> ? (#/release label))
> ;->;Compiler warnings :
> ;->;?? In an anonymous lambda form at position 57: Undeclared free variable
> MY-BUTTON
> ;->NIL ?
> 
> ;;; nothing shows in my-window1
> 
> ;; Second sequence with my-window2 for my-window1
> ;; switching adding the subview and showing the window
> ;; (a) allocate my-window2
> ;; (b) equip it with attributes
> ;; (f) add my-button as a subview to my-window2
> ;; (c) show my-window2
> 
> (setf my-window2 (#/alloc (@class ns-window)))
> ;->#<NS-WINDOW [uninitialized] (#x35D8C50)>
> (ns:with-ns-rect (r 100 100 400 300)
> ? (#/initWithContentRect:styleMask:backing:defer:
> ?? my-window2
> ?? r
> ?? (logior? #$NSTitledWindowMask
> ??????????? #$NSClosableWindowMask ?
> ??????????? #$NSMiniaturizableWindowMask
> ??????????? #$NSResizableWindowMask)
> ?? #$NSBackingStoreBuffered
> ?? #$NO))
> ;->;Compiler warnings :
> ;->;?? In an anonymous lambda form at position 93: Undeclared free variable
> MY-WINDOW2
> ;->#<NS-WINDOW <NSWindow 0x035d8c50> (#x35D8C50)>? ?
> (#/addSubview: (#/contentView my-window2) my-button)
> ;->NIL
> (#/makeKeyAndOrderFront: my-window2 nil)? ?
> ;->NIL
> 
> ;;; There is my-window2 complete with button which shows Hello!
> ;;; Hence the label was indeed added to the button under
> ;;; the First sequence.
> 
> ::: It seems that I can assemble a composite view and then successfully
> display
> ;;; the whole thing, but not display part of it and then add the rest. But
> what
> ;;; follows has me wondering about such a simple dichotomy ..
> 
> ;;; Third sequence
> ;;; Now some code from http://trac.clozure.com/ccl/wiki/CocoaBridge
> ;;; but operating in the :ccl package still
> (defclass red-view (ns:ns-view)
> ? ()
> ? (:metaclass ns:+ns-object))
> ;->#<OBJC:OBJC-CLASS RED-VIEW (#x35EA810)>
> (objc:defmethod (#/drawRect: :void) ((self red-view) (rect :<NSR>ect))
> ? (#/set (#/redColor ns:ns-color))
> ? (#_NSRectFill (#/bounds self)))
> ;->|-[RedView drawRect:]|
> (defun show-red-window ()
> ? (ccl::with-autorelease-pool
> ?? (let* ((rect (ns:make-ns-rect 0 0 300 300))
> ?? ?? (w (make-instance 'ns:ns-window
> ?? ??? ??? ???? :with-content-rect rect
> ?? ??? ??? ???? :style-mask (logior #$NSTitledWindowMask
> ?? ??? ??? ??? ??? ??????? #$NSClosableWindowMask
> ?? ??? ??? ??? ??? ??????? #$NSMiniaturizableWindowMask)
> ?? ??? ??? ???? :backing #$NSBackingStoreBuffered
> ?? ??? ??? ???? :defer t)))
> ???? (#/setTitle: w #@"Red")
> ???? (#/setContentView: w (#/autorelease (make-instance 'red-view)))
> ???? (#/center w)
> ???? (#/orderFront: w nil)
> ???? (#/contentView w))))
> ;->SHOW-RED-WINDOW?? ?
> (defmacro with-focused-view (view &body forms)
> ? `(when (#/lockFocusIfCanDraw ,view)
> ???? (unwind-protect
> ?? ?? (progn , at forms)
> ?????? (#/unlockFocus ,view)
> ?????? (#/flushGraphics (#/currentContext ns:ns-graphics-context))
> ?????? (#/flushWindow (#/window ,view)))))
> ;->WITH-FOCUSED-VIEW
> (setf *v* (show-red-window))
> ;->#<RED-VIEW <RedView[0x45726578] frame: {{3, 3}, {300, 300}}>
> (#x45726578)>
> 
> ;;; there is the red window
> 
> ;;; Modify the "More drawing" code of this example to
> ;;; (1) draw a line diagonally across the view, and
> ;;; (2) omit the "hello world" text
> 
> (with-focused-view *v*
> ??? (let* ((path (#/bezierPath ns:ns-bezier-path)))
> ????? (#/moveToPoint: path (ns:make-ns-point 10 10))
> ????? (#/lineToPoint: path (ns:make-ns-point 300 300))
> ????? (#/stroke path)
> ????? ))
> ;;; now there is a diagonal line (lower left -> upper right)
> 
> ;;; Add my-button to this
> (#/addSubview: *v* my-button)
> 
> ;;; There is the button too (with "Hello!"), BUT with a piece of the
> ;;; diagonal line missing (from (0,0) to just above the "e"
> ;;; on the button.
> 
> ;;; Thus I have been able to make a piece of a view, show it, and
> ;;; then add to it, unlike what happened in the First sequence.
> 
> ;;; So is it possible that my-button really was drawn in my first sequence,
> ;;; but in the background colour of its window ?
> 
> ;; add a title to the button
> (#/setTitle: my-button #@"Zootlewirdle")
> ;;;the title has changed, showing just the quartet ("rdle")
> ;;; so that I can modify part of an already displayed view.
> 
> Again, thanks for any and all assistance.? ??? ??? ??? ??? ?
> 
>



More information about the Openmcl-devel mailing list