Module: environment-framework Synopsis: Environment Framework Author: Andy Armstrong, Jason Trenouth, Chris Page Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// PROTOCOL define open generic frame-reusable? (frame :: ) => (reusable? :: ); define open generic frame-reusable?-setter (resuable? :: , frame :: ) => (reusable? :: ); define open generic reuse-frames? (class :: subclass()) => (reuse-frames? :: ); define open generic reuse-frames?-setter (value :: , class :: subclass()) => (value :: ); define open generic call-in-environment-frame (function :: , portd :: , class :: subclass(), #rest initargs, #key, #all-keys) => (); // obsolete (use ensure-environment-frame) define open generic find-environment-frame (portd :: , class :: subclass(), #key, #all-keys) => (); define open generic ensure-environment-frame (portd :: , class :: subclass(), #key, #all-keys) => (); define open generic reuse-environment-frame (portd :: , class :: subclass(), #key, #all-keys) => (frame :: false-or()); define open generic find-matching-frames (portd :: , class :: subclass(), #key, #all-keys) => (frames :: ); define open generic choose-matching-frame (portd :: , class :: subclass(), #key, #all-keys) => (frame :: false-or()); define open generic choose-environment-frame (portd :: , class :: subclass(), #key, #all-keys) => (frame :: false-or()); define open generic choose-current-frame (portd :: , class :: subclass(), #key, #all-keys) => (frame :: false-or()); define open generic current-environment-frame (portd :: ) => (frame :: false-or()); define open generic reuse-matching-frame? (portd :: , frame :: , class :: subclass(), #key, #all-keys) => (reuse? :: ); define open generic choose-frame (portd :: , class :: subclass(), frames :: , #key, #all-keys) => (frame :: false-or()); define open generic reinitialize-frame (frame :: , #key, #all-keys) => (); define open generic fork-environment-frame (portd :: , class :: subclass(), #key, #all-keys) => (); define open generic make-environment-frame (portd :: , class :: subclass(), #key, #all-keys) => (frame :: ); define open generic start-environment-frame (frame :: ) => (); define open generic make-environment-thread (portd :: , class :: subclass(), #key name :: , function :: ) => (); define open generic do-environment-frame (frame :: , function :: false-or()) => (); define open generic frame-thread-name (portd :: , class :: subclass()) => (thread-name :: ); /// (internal) define constant :: = type-union(, , ); /// PORT-DESIGNATOR-PORT (internal) //--- cpage: 1998.10.09 These are currently unused. Why? Should we remove them altogether? /* define method port-designator-port (_port :: ) => (_port :: ) _port end method; define method port-designator-port (frame :: ) => (_port :: ) port(frame) end method; define method port-designator-port (display :: ) => (_port :: ) port(display) end method; */ /// *REUSE-FRAMES?* (internal) define variable *reuse-frames?* = #t; /// *FRAME-REUSABLE?-DEFAULT* (internal) define variable *frame-reusable?-default* = #t; /// (environment-framework) define open abstract class () slot frame-reusable? :: = *frame-reusable?-default*, init-keyword: reusable?:; end class ; /// FRAME-REUSABLE? (environment-framework) define method frame-reusable? (frame :: ) => (reusable? :: ) #t end method frame-reusable?; /// REUSE-FRAMES? (environment-framework) define method reuse-frames? (class :: subclass()) => (reuse-frames? :: ) *reuse-frames?* end method reuse-frames?; define method reuse-frames?-setter (value :: , class :: subclass()) => (value :: ) *reuse-frames?* := value end method reuse-frames?-setter; /// FRAME-REUSE MESSAGE CLASSES (environment-framework) define open abstract class () end class ; define class () constant slot message-frame :: false-or() = #f, init-keyword: frame:; end class ; /// FIND-ENVIRONMENT-FRAME (environment-framework) /// /// obsolete (use ensure-environment-frame) define method find-environment-frame (portd :: , class :: subclass(), #rest initargs, #key) => () apply(ensure-environment-frame, portd, class, initargs) end method find-environment-frame; /// ENSURE-ENVIRONMENT-FRAME (environment-framework) define method ensure-environment-frame (portd :: , class :: subclass(), #rest initargs, #key) => () apply(reuse-environment-frame, portd, class, initargs) | apply(fork-environment-frame, portd, class, initargs) end method ensure-environment-frame; /// REUSE-ENVIRONMENT-FRAME (environment-framework) define method reuse-environment-frame (portd :: , class :: subclass(), #rest initargs, #key) => (frame :: false-or()) if (reuse-frames?(class)) let frame = apply(choose-environment-frame, portd, class, initargs); if (frame) apply-in-frame(frame, reinitialize-frame, frame, initargs); call-in-frame(frame, do-environment-frame, frame, *environment-frame-function*); frame else #f end if; else #f end if; end method; /// CHOOSE-ENVIRONMENT-FRAME (environment-framework) define method choose-environment-frame (portd :: , class :: subclass(), #rest initargs, #key) => (frame :: false-or()) apply(choose-current-frame, portd, class, initargs) | apply(choose-matching-frame, portd, class, initargs); end method; /// CHOOSE-CURRENT-FRAME (environment-framework) define method choose-current-frame (portd :: , class :: subclass(), #rest initargs, #key) => (frame :: false-or()) let frame = current-environment-frame(portd); if (frame & apply(reuse-matching-frame?, portd, frame, class, initargs)) frame end if; end method; /// CHOOSE-MATCHING-FRAME (environment-framework) define method choose-matching-frame (portd :: , class :: subclass(), #rest initargs, #key) => (frame :: false-or()) let frames = apply(find-matching-frames, portd, class, initargs); apply(choose-frame, portd, class, frames, initargs); end method; /// $FORKED-FRAMES-LOCK (internal) define constant $forking-environment-frames-lock = make(); /// WAIT-FOR-FORKED-FRAMES (internal) define macro wait-for-forking-frames { wait-for-forking-frames (?class:expression) ?body:body end } => { with-lock($forking-environment-frames-lock) let forking-frame = find-forking-environment-frame(?class); if (forking-frame) if (wait-for(forking-frame.environment-frame-notification)) ?body end if; else ?body end if; end } end macro; /// $FORKING-ENVIRONMENT-FRAMES (internal) define variable *forking-environment-frames* :: = make(); /// (internal) define sealed class () sealed slot environment-frame-class :: subclass(), required-init-keyword: class:; sealed slot environment-frame-count :: = 0; sealed slot environment-frame-notification :: = make(, lock: $forking-environment-frames-lock); end class ; define sealed domain make (subclass()); define sealed domain initialize (); /// NOTE-ENVIRONMENT-FRAME-FORKING (internal) define method note-environment-frame-forking (class :: subclass()) => () with-lock($forking-environment-frames-lock) let forking-frame = find-forking-environment-frame(class) | make-forking-environment-frame(class); forking-frame.environment-frame-count := forking-frame.environment-frame-count + 1; end; end method; /// MAKE-FORKING-ENVIRONMENT-FRAME (internal) define method make-forking-environment-frame (class :: subclass()) => (forking-frame :: ) let forking-frame = make(, class: class); *forking-environment-frames* := add!(*forking-environment-frames*, forking-frame); forking-frame end method; /// FIND-FORKING-ENVIRONMENT-FRAME (internal) define method find-forking-environment-frame (class :: subclass()) => (forking-frame :: false-or()) block (found) for (forking-frame in *forking-environment-frames*) // NB test both ways around in case MAKE makes a subclass // of requested class. if (subtype?(forking-frame.environment-frame-class, class) | subtype?(class, forking-frame.environment-frame-class)) found(forking-frame); end if; end for; end block; end method; /// NOTE-ENVIRONMENT-FRAME-FORKED (internal) define method note-environment-frame-forked (class :: subclass()) => () with-lock($forking-environment-frames-lock) let forking-frame = find-forking-environment-frame(class); if (forking-frame) forking-frame.environment-frame-count := forking-frame.environment-frame-count - 1; if (forking-frame.environment-frame-count = 0) *forking-environment-frames* := remove!(*forking-environment-frames*, forking-frame); release-all(forking-frame.environment-frame-notification) end if; end if; end; end method; /// FIND-MATCHING-FRAMES (environment-framework) define method find-matching-frames (portd :: , class :: subclass(), #rest initargs, #key) => (frames :: ) let frames :: = #(); wait-for-forking-frames (class) // The result will contain the frames in Z order from front to back, // but we do this iteration from the bottom up because 'add!' on a // list adds to the front... do-frames (method (frame :: ) => () when (apply(reuse-matching-frame?, portd, frame, class, initargs)) frames := add!(frames, frame) end end method, z-order: #"bottom-up"); end; frames end method find-matching-frames; /// REUSE-MATCHING-FRAME? (environment-framework) define method reuse-matching-frame? (portd :: , frame :: , class :: subclass(), #rest initargs, #key) => (reuse? :: ) instance?(frame, class) & frame-reusable?(frame) end method; /// CHOOSE-FRAME (environment-framework) define method choose-frame (portd :: , class :: subclass(), frames :: , #rest initargs, #key) => (frame :: false-or()) ~empty?(frames) & frames[0]; end method choose-frame; /// REINITIALIZE-FRAME (environment-framework) define method reinitialize-frame (frame :: , #rest initargs, #key) => () #f end method reinitialize-frame; /// FORK-ENVIRONMENT-FRAME (environment-framework) define method fork-environment-frame (portd :: , class :: subclass(), #rest initargs, #key) => () note-environment-frame-forking(class); fork-environment-function(portd, class, method () let frame = #f; block () frame := apply(make-environment-frame, portd, class, initargs); cleanup unless (frame) note-environment-frame-forked(class); end unless; end block; end method); end method; /// HANDLE-EVENT define method handle-event (frame :: , event :: ) => () next-method(); note-environment-frame-forked(object-class(frame)); end method; /// FORK-ENVIRONMENT-FUNCTION //---*** cpage: 1998.10.09 TESTING: Use one thread and event queue for all frames. // Set this to true to turn this experimental code on. define variable *one-thread-for-all-frames* :: = #f; define method fork-environment-function (portd :: , class :: subclass(), frame-maker :: ) => () // Must ensure we evaluate *environment-frame-function* in the "calling" // thread, rather than inside the local method below, which will run on // the new thread, where it will always be #f! let function = *environment-frame-function*; local method make-and-start-frame () let frame = frame-maker(); call-in-frame(frame, do-environment-frame, frame, function); start-environment-frame(frame); end method; //---*** cpage: 1998.10.09 TESTING: Use one thread and event queue for all frames. if (*one-thread-for-all-frames*) make-and-start-frame(); else make-environment-thread(portd, class, name: frame-thread-name(portd, class), function: frame-thread-function(portd, class, make-and-start-frame)); end; end method; /// MAKE-ENVIRONMENT-FRAME (environment-framework) //---*** cpage: 1998.10.09 TESTING: Use one thread and event queue for all frames. define constant $event-queue :: = make(); define method make-environment-frame (portd :: , class :: subclass(), #rest initargs, #key) => (frame :: ) //---*** cpage: 1998.10.09 TESTING: Use one thread and event queue for all frames. if (*one-thread-for-all-frames*) apply(make, class, event-queue: $event-queue, initargs) else apply(make, class, initargs) end end method make-environment-frame; /// START-ENVIRONMENT-FRAME (environment-framework) define method start-environment-frame (frame :: ) => () start-frame(frame) end method; define method start-environment-frame (frame :: ) => () start-dialog(frame) end method; /// Support for managing the thread state define variable *environment-thread-count* :: = 0; define constant $environment-thread-lock :: = make(); define constant $final-thread-notification :: = make(, lock: $environment-thread-lock); /// MAKE-ENVIRONMENT-THREAD (environment-framework) define method make-environment-thread (portd :: , class :: subclass(), #key name :: , function :: ) => () duim-debug-message("Creating thread '%s' for frame class %=", name, class); with-lock ($environment-thread-lock) make(, name: name, function: function); // Count the new environment thread *environment-thread-count* := *environment-thread-count* + 1; end end method make-environment-thread; /// FRAME-THREAD-FUNCTION (environment-framework) define method frame-thread-function (portd :: , class :: subclass(), function :: ) => (new-function :: ) method () block () with-abort-restart () function() end cleanup with-lock ($environment-thread-lock) // This thread is now gone, so notice that and quit // if this is the last environment thread *environment-thread-count* := *environment-thread-count* - 1; when (*environment-thread-count* = 0) exit-environment(0) end end end end method end method frame-thread-function; /// WAIT-FOR-SHUTDOWN (environment-framework) define variable *exit-code* :: = 0; define function wait-for-shutdown () => (exit-code :: ) with-lock ($environment-thread-lock) wait-for($final-thread-notification) end; *exit-code* end function wait-for-shutdown; /// EXIT-ENVIRONMENT (environment-framework) // NB: Must be called with $environment-thread-lock held! define function exit-environment (status-code :: ) => () *exit-code* := status-code; release($final-thread-notification) end function exit-environment; /// $THREAD-COUNT-TABLE (internal) define variable $thread-count-table = make(); /// FRAME-CLASS-TITLE (environment-framework) /// /// ---*** Maybe put this in DUIM define open generic frame-class-title (class :: subclass()) => (title :: ); define method frame-class-title (class :: subclass()) => (title :: ) format-to-string("%= Thread", class); end method; /// FRAME-THREAD-NAME (environment-framework) define method frame-thread-name (portd :: , class :: subclass()) => (thread-name :: ) let title = frame-class-title(class); let key = as(, title); let number = element($thread-count-table, key, default: 1); $thread-count-table[key] := number + 1; format-to-string("%s %d", title, number) end method frame-thread-name; /// *CURRENT-ENVIRONMENT-FRAME* (internal) define thread variable *current-environment-frame* :: false-or()= #f; /// CURRENT-ENVIRONMENT-FRAME (environment-framework) define method current-environment-frame (frame :: ) => (frame :: ) frame end method current-environment-frame; define method current-environment-frame (portd :: ) => (frame :: false-or()) *current-environment-frame* end method current-environment-frame; /// WITH-CURRENT-ENVIRONMENT-FRAME (environment-framework) define macro with-current-environment-frame { with-current-environment-frame (?frame:expression) ?body:body end } => { dynamic-bind (*current-environment-frame* = ?frame) ?body end } end macro; /// *ENVIRONMENT-FRAME-FUNCTION* (internal) define thread variable *environment-frame-function* :: false-or() = #f; /// DO-ENVIRONMENT-FRAME (internal) define method do-environment-frame (frame :: , function == #f) => () broadcast($environment-channel, make(, frame: frame)); deiconify-frame(frame); raise-frame(frame); end method; define method do-environment-frame (frame :: , function :: ) => () broadcast($environment-channel, make(, frame: frame)); function(frame); end method; /// WITH-ENVIRONMENT-FRAME (environment-framework) define macro with-environment-frame { with-environment-frame (?frame:name = ?portd:expression, ?class:expression, ?initargs:*) ?body:body end } => { call-in-environment-frame(method (?frame) ?body end method, ?portd, ?class, ?initargs) } end macro; /// CALL-IN-ENVIRONMENT-FRAME (environment-framework) define method call-in-environment-frame (function :: , portd :: , class :: subclass(), #rest initargs, #key) => () dynamic-bind (*environment-frame-function* = function) apply(find-environment-frame, portd, class, initargs); end; end method;