## xevent-router-ximp.pkg
#
# Replaces
src/lib/x-kit/xclient/src/window/xsocket-to-hostwindow-router-old.pkg#
#
src/lib/x-kit/xclient/src/window/xevent-router-ximp.pkg#
src/lib/x-kit/xclient/src/window/keymap-ximp.pkg#
# For the big picture see the imp dataflow diagrams in
#
#
src/lib/x-kit/xclient/src/window/xclient-ximps.pkg#
# The xsequencer_ximp is responsible for matching
# replies read from the X with requests sent
# to it.
#
# All requests to the X-server go through the xsequencer_ximp,
# as do all replies from the X-server.
#
# The xsequencer_ximp communicates on five fixed channels:
#
# plea_mailslot -- request messages from clients
# from_x_mailslot -- reply, error and event messages from the X server (via the input buffer)
# to_x_mailslot -- requests messages to the X server (via the output buffer)
# xevent_mailslot -- X-events to the X-event buffer (and thence to clients)
# error_sink_mailslot -- errors to the error handler
#
# In addition, the xsequencer_ximp sends replies
# to clients on the reply channel that was
# bundled with the request.
#
# We maintain a pending-reply queue of requests sent
# to the X server for which replies are expected but
# not yet received.
# We represent it using the usual two-list arrangement,
# writing new entries to the rear list while reading them
# from the front list; when the front list is empty we
# reverse the rear list and make it the new front list.
# Compiled by:
#
src/lib/x-kit/xclient/xclient-internals.sublibstipulate
include package threadkit; # threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg #
#
package un = unt; # unt is from
src/lib/std/unt.pkg package v1u = vector_of_one_byte_unts; # vector_of_one_byte_unts is from
src/lib/std/src/vector-of-one-byte-unts.pkg package w2v = wire_to_value; # wire_to_value is from
src/lib/x-kit/xclient/src/wire/wire-to-value.pkg package g2d = geometry2d; # geometry2d is from
src/lib/std/2d/geometry2d.pkg package xtr = xlogger; # xlogger is from
src/lib/x-kit/xclient/src/stuff/xlogger.pkg package hx = hash_xid; # hash_xid is from
src/lib/x-kit/xclient/src/stuff/hash-xid.pkg package r2k = xevent_router_to_keymap; # xevent_router_to_keymap is from
src/lib/x-kit/xclient/src/window/xevent-router-to-keymap.pkg package xwp = windowsystem_to_xevent_router; # windowsystem_to_xevent_router is from
src/lib/x-kit/xclient/src/window/windowsystem-to-xevent-router.pkg package xes = xevent_sink; # xevent_sink is from
src/lib/x-kit/xclient/src/wire/xevent-sink.pkg package xt = xtypes; # xtypes is from
src/lib/x-kit/xclient/src/wire/xtypes.pkg package xet = xevent_types; # xevent_types is from
src/lib/x-kit/xclient/src/wire/xevent-types.pkg package xm = xt::xid_map; # Map where key::Key == xt::Xid.
#
trace = xtr::log_if xtr::io_logging 0; # Conditionally write strings to tracing.log or whatever.
herein
# This impset is typically instantiated by:
#
#
src/lib/x-kit/xclient/src/window/xsession-ximps.pkg package xevent_router_ximp
: (weak) Xevent_Router_Ximp # Xevent_Router_Ximp is from
src/lib/x-kit/xclient/src/window/xevent-router-ximp.api {
Envelope_Route
= ENVELOPE_ROUTE_END xt::Window_Id
| ENVELOPE_ROUTE (xt::Window_Id, Envelope_Route)
;
Window_Info
=
WINDOW_INFO
{
window_id: xt::Window_Id, # 29-bit X id for this particular window.
route: xwp::Envelope_Route, # Path needed to reach this window, starting at its hostwindow.
parent_info: Null_Or( Window_Info ),
#
children: Ref( List(Window_Info) ),
lock: Ref( Bool ),
site: Ref( g2d::Box ),
#
seen_first_expose: Ref( Bool ), # We set this TRUE on first EXPOSE event.
seen_first_expose_oneshot: Oneshot_Maildrop(Void), # We set this on first EXPOSE event.
#
xevent_sink: (xwp::Envelope_Route, xet::x::Event) -> Void # Where to send events headed for this window.
};
Xevent_Router_Ximp_State # Holds all nonephemeral mutable state maintained by ximp.
=
{ wid_to_winfo: Ref( xm::Map( Window_Info ) ), # "wid_to_winfo" == "window id to window info map"
#
# wid_to_pleas: Ref( xm::Map( List(Client_Plea) ) ), # "wid_to_pleas" == "window id to window pleas map"
wid_to_1shot: Ref( xm::Map( Oneshot_Maildrop(Void) ) ) # "wid_to_1shot" == "window id to oneshot map"
};
Imports = { # Ports we use which are exported by other imps.
xevent_router_to_keymap: r2k::Xevent_Router_To_Keymap,
window_property_xevent_sink: xes::Xevent_Sink, #
selection_xevent_sink: xes::Xevent_Sink #
};
Me_Slot = Mailslot( { imports: Imports,
me: Xevent_Router_Ximp_State,
run_gun': Run_Gun,
end_gun': End_Gun
}
);
Exports = { # Ports we export for use by other imps.
xevent_sink: xes::Xevent_Sink, # For xevents from xserver via inbuf, sequencer and decoder ximp.
windowsystem_to_xevent_router: xwp::Windowsystem_To_Xevent_Router # Requests from widget/application code.
};
Option = MICROTHREAD_NAME String; #
Xevent_Router_Egg = Void -> (Exports, (Imports, Run_Gun, End_Gun) -> Void);
# The various things we can
# do with a given X event:
#
Xevent_Action
#
= SEND_TO_WINDOW xt::Window_Id # Forward event to given window via all of its ancestors from hostwindow down.
| NOTE_SITE_CHANGE_AND_SEND_TO_WINDOW (xt::Window_Id, g2d::Box)
# Note new size+position of window, then forward event normally.
| NOTE_WINDOW_DESTRUCTION xt::Window_Id
| SEND_TO_KEYMAP_IMP
# This appears to be unused at present.
| SEND_TO_WINDOW_PROPERTY_IMP
| SEND_TO_SELECTION_IMP
| SEND_TO_ALL_WINDOWS
# So everyone hears about changes in modifier key, keyboard and pointer mappings.
| IGNORE
| NOTE_NEW_WINDOW
{ parent_window_id: xt::Window_Id,
created_window_id: xt::Window_Id,
box: g2d::Box
}
;
Xevent_Q = Mailqueue( xet::x::Event );
Runstate = { # These values will be statically globally visible throughout the code body for the imp.
me: Xevent_Router_Ximp_State, #
imports: Imports, # Ximps to which we send requests.
to: Replyqueue, # The name makes foo::pass_something(imp) to {. ... } syntax read well.
#
end_gun': End_Gun, # We shut down the microthread when this fires.
xevent_q: Xevent_Q #
};
Client_Q = Mailqueue( Runstate -> Void );
# Discard instances of an X-event that
# are the product of SubstructureNotify,
# instead of StructureNotify.
#
fun ignore_substructure_notify_xevents (window_id1, window_id2)
=
if (xt::same_xid (window_id1, window_id2)) SEND_TO_WINDOW window_id1;
else IGNORE;
fi;
# Decide what action to take for given X event. Here
#
# event_window_id
#
# is the window corresponding to the widget which
# should actually handle the event, as determined
# by the X server; the X server algorithm is
# described on pages 76-77 of
#
# http://mythryl.org/pub/exene/X-protocol-R6.pdf
#
fun pick_xevent_action (xet::x::KEY_PRESS { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::KEY_RELEASE { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::BUTTON_PRESS { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::BUTTON_RELEASE { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::MOTION_NOTIFY { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::ENTER_NOTIFY { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::LEAVE_NOTIFY { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::FOCUS_IN { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
pick_xevent_action (xet::x::FOCUS_OUT { event_window_id, ... } ) => SEND_TO_WINDOW event_window_id;
# pick_xevent_action (xet::x::KeymapNotify {, ... } ) =
# pick_xevent_action (xet::x::GraphicsExpose ??
# pick_xevent_action (xet::x::NoExpose {, ... } ) =
# pick_xevent_action (xet::x::MapRequest {, ... } ) =
# pick_xevent_action (xet::x::ConfigureRequest {, ... } ) =
# pick_xevent_action (xet::x::ResizeRequest {, ... } ) =
# pick_xevent_action (xet::x::CirculateRequest {, ... } ) =
pick_xevent_action (xet::x::EXPOSE { exposed_window_id, ... } ) => SEND_TO_WINDOW exposed_window_id;
pick_xevent_action (xet::x::VISIBILITY_NOTIFY { changed_window_id, ... } ) => SEND_TO_WINDOW changed_window_id;
pick_xevent_action (xet::x::CREATE_NOTIFY { parent_window_id, created_window_id, box, ... } )
=>
NOTE_NEW_WINDOW { parent_window_id, created_window_id, box };
pick_xevent_action (xet::x::DESTROY_NOTIFY { event_window_id, destroyed_window_id, ... } )
=>
xt::same_xid (event_window_id, destroyed_window_id)
##
?? NOTE_WINDOW_DESTRUCTION event_window_id # Remove window from registry.
:: SEND_TO_WINDOW event_window_id; # Report to parent that child is dead.
pick_xevent_action (xet::x::UNMAP_NOTIFY { event_window_id, unmapped_window_id, ... } )
=>
ignore_substructure_notify_xevents (event_window_id, unmapped_window_id);
pick_xevent_action (xet::x::MAP_NOTIFY { event_window_id, mapped_window_id, ... } )
=>
ignore_substructure_notify_xevents (event_window_id, mapped_window_id);
pick_xevent_action (xet::x::REPARENT_NOTIFY _)
=>
IGNORE;
pick_xevent_action (xet::x::CONFIGURE_NOTIFY { event_window_id, configured_window_id, box, ... } )
=>
case (ignore_substructure_notify_xevents (event_window_id, configured_window_id))
#
SEND_TO_WINDOW _ => NOTE_SITE_CHANGE_AND_SEND_TO_WINDOW (configured_window_id, box);
_ => IGNORE;
esac;
pick_xevent_action (xet::x::GRAVITY_NOTIFY { event_window_id, moved_window_id, ... } )
=>
ignore_substructure_notify_xevents (event_window_id, moved_window_id);
pick_xevent_action (xet::x::CIRCULATE_NOTIFY { event_window_id, circulated_window_id, ... } )
=>
ignore_substructure_notify_xevents (event_window_id, circulated_window_id);
pick_xevent_action (xet::x::PROPERTY_NOTIFY _) => SEND_TO_WINDOW_PROPERTY_IMP; # We may have other uses of PropertyNotify someday.
pick_xevent_action (xet::x::SELECTION_CLEAR _) => SEND_TO_SELECTION_IMP;
pick_xevent_action (xet::x::SELECTION_REQUEST _) => SEND_TO_SELECTION_IMP;
pick_xevent_action (xet::x::SELECTION_NOTIFY _) => SEND_TO_SELECTION_IMP;
pick_xevent_action (xet::x::COLORMAP_NOTIFY { window_id, ... } ) => SEND_TO_WINDOW window_id;
pick_xevent_action (xet::x::CLIENT_MESSAGE { window_id, ... } ) => SEND_TO_WINDOW window_id;
pick_xevent_action xet::x::MODIFIER_MAPPING_NOTIFY => SEND_TO_ALL_WINDOWS;
pick_xevent_action (xet::x::KEYBOARD_MAPPING_NOTIFY _) => SEND_TO_ALL_WINDOWS;
pick_xevent_action xet::x::POINTER_MAPPING_NOTIFY => SEND_TO_ALL_WINDOWS;
pick_xevent_action e => { log::fatal (string::cat [ "[xsocket-to-topwin: unexpected ", xevent_to_string::xevent_name e, " event]\n"]);
IGNORE;
};
end;
# Define a tracelogging version of
#
# pick_xevent_action
#
stipulate
#
fun xevent_action_to_string (SEND_TO_WINDOW w)
=>
("SEND_TO_WINDOW(" + xt::xid_to_string w + ")");
xevent_action_to_string (NOTE_SITE_CHANGE_AND_SEND_TO_WINDOW (w,_))
=>
("NOTE_SITE_CHANGE_AND_SEND_TO_WINDOW(" + xt::xid_to_string w + ")");
xevent_action_to_string (NOTE_NEW_WINDOW { parent_window_id, created_window_id, box } )
=>
string::cat
[
"NOTE_NEW_WINDOW { parent=", xt::xid_to_string parent_window_id,
", new_window=", xt::xid_to_string created_window_id,
"}"
];
xevent_action_to_string (NOTE_WINDOW_DESTRUCTION w) => ("NOTE_WINDOW_DESTRUCTION(" + xt::xid_to_string w + ")");
xevent_action_to_string SEND_TO_KEYMAP_IMP => "SEND_TO_KEYMAP_IMP";
xevent_action_to_string SEND_TO_WINDOW_PROPERTY_IMP => "SEND_TO_WINDOW_PROPERTY_IMP";
xevent_action_to_string SEND_TO_SELECTION_IMP => "SEND_TO_SELECTION_IMP";
xevent_action_to_string SEND_TO_ALL_WINDOWS => "SEND_TO_ALL_WINDOWS";
xevent_action_to_string IGNORE => "IGNORE";
end;
herein
#
pick_xevent_action
=
\\ xevent
=
{ xevent_action = pick_xevent_action xevent;
trace {.
#
cat [ "xsocket_to_hostwindow_router: ", xevent_to_string::xevent_name xevent,
" => ", xevent_action_to_string xevent_action
];
};
xevent_action;
};
end;
# -DEBUG
# fun set_window_subtree_locks_to # Commented out 2014-06-28 because it is never referenced.
# (bool: Bool)
# =
# set
# where
# fun set (WINDOW_INFO { lock, children, ... } )
# =
# { lock := bool;
# set_list *children;
# }
#
# also
# fun set_list (wd ! r)
# =>
# { set wd;
# set_list r;
# };
#
# set_list []
# =>
# ();
# end;
# end;
fun run ( client_q: Client_Q, # Requests from x-widgets and such
gui_startup_complete_oneshot,
#
runstate as
{ # These values will be statically globally visible throughout the code body for the imp.
me: Xevent_Router_Ximp_State, #
imports: Imports, # Ximps to which we send requests.
to: Replyqueue, # The name makes foo::pass_something(imp) to {. ... } syntax read well.
#
end_gun': End_Gun, # We shut down the microthread when this fires.
xevent_q: Xevent_Q #
}
)
=
loop ()
where
fun loop () # Outer loop for the imp.
=
{ do_one_mailop' to [
#
(end_gun' ==> shut_down_xevent_to_window_imp'),
(take_from_mailqueue' client_q ==> do_client_plea),
(take_from_mailqueue' xevent_q ==> do_xevent)
];
loop ();
}
where
fun do_client_plea thunk
=
thunk runstate;
fun shut_down_xevent_to_window_imp' ()
=
thread_exit { success => TRUE }; # Will not return.
#
fun get_winfo window_id
=
{
# printf "get_winfo -- xevent-router-ximp.pkg\n";
case (xm::get (*me.wid_to_winfo, window_id))
#
THE winfo => {
# printf "get_winfo found entry! -- xevent-router-ximp.pkg\n";
winfo;
};
NULL => { log::fatal ("window_id not found -- do_plea (plea::GET_WINDOW_SITE in xevent-router-ximp.pkg");
raise exception DIE ""; # Should not get here.
};
esac;
};
#
fun note_new_subwindow (parent_window_id, child_window_id, box)
=
{ parent_info = get_winfo parent_window_id;
#
parent_info -> WINDOW_INFO { route, xevent_sink, children, lock, ... };
#
fun extend_route (xwp::ENVELOPE_ROUTE_END window_id) => xwp::ENVELOPE_ROUTE (window_id, xwp::ENVELOPE_ROUTE_END child_window_id);
extend_route (xwp::ENVELOPE_ROUTE (window_id, route)) => xwp::ENVELOPE_ROUTE (window_id, extend_route route);
end;
child_route = extend_route route;
child_info
=
WINDOW_INFO
{
window_id => child_window_id,
route => child_route,
site => REF box,
parent_info => THE parent_info,
children => REF [],
lock => REF *lock,
#
xevent_sink,
#
seen_first_expose => REF( FALSE ),
seen_first_expose_oneshot => (make_oneshot_maildrop ())
};
children := child_info ! *children;
me.wid_to_winfo := xm::set (*me.wid_to_winfo, child_window_id, child_info);
};
#
fun note_site_change (window_id, box)
=
{ (get_winfo window_id) -> WINDOW_INFO { site, ... };
#
site := box;
};
#
fun route_xevent_per_window_info (xevent, WINDOW_INFO { route, xevent_sink, ... } )
=
xevent_sink (route, xevent);
#
fun route_xevent_to_window_id (xevent, window_id)
=
{
# printf "route_xevent_to_window_idAAA -- xevent-router-ximp.pkg\n";
route_xevent_per_window_info (xevent, get_winfo window_id);
};
#
fun do_xevent xevent
=
{
# printf "do_xevent/AAA -- xevent-router-ximp.pkg\n";
case (pick_xevent_action xevent)
#
SEND_TO_WINDOW window_id
=>
{
# printf "do_xevent/SEND_TO_WINDOW -- xevent-router-ximp.pkg\n";
route_xevent_to_window_id (xevent, window_id);
};
NOTE_SITE_CHANGE_AND_SEND_TO_WINDOW (window_id, box)
=>
{ note_site_change (window_id, box); # Window has changed size and/or position.
#
route_xevent_to_window_id (xevent, window_id);
};
NOTE_NEW_WINDOW { parent_window_id, created_window_id, box }
=>
{ note_new_subwindow (parent_window_id, created_window_id, box);
#
route_xevent_to_window_id (xevent, parent_window_id);
};
NOTE_WINDOW_DESTRUCTION window_id
=>
case (xm::get_and_drop (*me.wid_to_winfo, window_id))
#
(new_wid_to_winfo, THE (window_info as WINDOW_INFO { parent_info => THE (WINDOW_INFO { children, ... } ), ... } ))
=>
{ me.wid_to_winfo := new_wid_to_winfo;
#
children := remove_child *children;
route_xevent_per_window_info (xevent, window_info);
}
where
fun remove_child ((window_info' as WINDOW_INFO { window_id => window_id', ... } ) ! rest)
=>
if (xt::same_xid (window_id', window_id)) rest ;
else (window_info' ! (remove_child rest));
fi;
remove_child []
=>
{ log::fatal ("Missing child -- do_xevent/NOTE_WINDOW_DESTRUCTION in xevent-router-ximp.pkg");
raise exception DIE ""; # Shouldn't get here.
};
end;
end;
(new_wid_to_winfo, THE window_info)
=>
{ me.wid_to_winfo := new_wid_to_winfo;
#
route_xevent_per_window_info (xevent, window_info);
};
(new_wid_to_winfo, NULL)
=>
{ log::fatal ("Missing window -- do_xevent/NOTE_WINDOW_DESTRUCTION in xevent-router-ximp.pkg");
raise exception DIE ""; # Shouldn't get here.
};
esac;
SEND_TO_KEYMAP_IMP
=>
{ log::fatal ("Unexpected SEND_TO_KEYMAP_IMP do_xevent in xevent-router-ximp.pkg]");
raise exception DIE "";
};
SEND_TO_WINDOW_PROPERTY_IMP => imports.window_property_xevent_sink.put_value xevent;
SEND_TO_SELECTION_IMP => imports.selection_xevent_sink.put_value xevent;
IGNORE => ();
SEND_TO_ALL_WINDOWS
=>
apply' (xm::vals_list *me.wid_to_winfo)
{. route_xevent_per_window_info (xevent, #window_info); };
esac; # fun do_xevent
};
end; # fun loop
end; # fun run
fun startup (reply_oneshot: Oneshot_Maildrop( (Me_Slot, Exports) )) () # Root fn of imp microthread. Note currying.
=
{ me_slot = make_mailslot () : Me_Slot;
#
xevent_sink = { put_value };
windowsystem_to_xevent_router
=
{ note_new_hostwindow,
get_window_site,
given_window_id_pass_site,
get_''seen_first_expose''_oneshot_of,
get_''gui_startup_complete''_oneshot_of
};
to = make_replyqueue();
put_in_oneshot (reply_oneshot, (me_slot, { xevent_sink, windowsystem_to_xevent_router })); # Return value from xevent_to_window_egg'().
(take_from_mailslot me_slot) # Imports from xevent_to_window_egg'().
->
{ me, imports, run_gun', end_gun' };
block_until_mailop_fires run_gun'; # Wait for the starting gun.
run (client_q, gui_startup_complete_oneshot, { me, xevent_q, imports, to, end_gun' }); # Will not return.
}
where
client_q = make_mailqueue (get_current_microthread()) : Client_Q;
xevent_q = make_mailqueue (get_current_microthread()) : Xevent_Q;
gui_startup_complete_oneshot
=
make_oneshot_maildrop (): Oneshot_Maildrop( Void );
#
# This maildrop exists to give application threads
# something to wait on before presuming that the
# GUI widgettree windows, threads etc are ready for action.
#
# Currently we set this when we first
# get an EXPOSE X event from the X server.
#
# ===============
# XXX SUCKO FIXME I'm hoping we can eliminate this with the new startup protocol. --CrT 2013-08-11
# ===============
fun note_new_hostwindow # PUBLIC.
(
window_id: xt::Window_Id,
window_site: g2d::Window_Site,
xevent_sink: (xwp::Envelope_Route, xet::x::Event) -> Void
)
=
put_in_mailqueue (client_q,
#
\\ ({ me, imports, ... }: Runstate)
=
{
# PLEA_NOTE_NEW_HOSTWINDOW arg
# fun do_plea (PLEA_NOTE_NEW_HOSTWINDOW (window_id, window_site, xevent_sink)) # Log a new top-level window.
# =>
# {
# I have the following in the oldworld, I'm not
# implementing that here partly out of laziness
# but mostly out of revulsion -- there must be
# something less awful.
#
# # Handle any prematurely-registered oneshot.
# # I'm not sure a hostwindow will ever get an
# # EXPOSE event, so this may be pointless:
# #
# oneshot = { oneshot = get_from_oneshot_map window_id;
# drop_from_oneshot_map window_id;
# THE oneshot;
# }
# except
# lib_base::NOT_FOUND
# =
# NULL;
me.wid_to_winfo
:=
xm::set (
*me.wid_to_winfo,
window_id,
WINDOW_INFO
{
window_id,
xevent_sink,
route => xwp::ENVELOPE_ROUTE_END window_id,
parent_info => NULL,
#
children => REF [],
lock => REF FALSE,
site => REF (g2d::site_to_box window_site),
#
seen_first_expose => REF( FALSE ),
seen_first_expose_oneshot => (make_oneshot_maildrop ())
}
);
}
);
fun get_winfo
(
me: Xevent_Router_Ximp_State,
window_id
)
=
{
printf "get_winfo -- xevent-router-ximp.pkg\n";
case (xm::get (*me.wid_to_winfo, window_id))
#
THE winfo => {
printf "get_winfo found entry! -- xevent-router-ximp.pkg\n";
winfo;
};
NULL => { log::fatal ("window_id not found -- do_plea (plea::GET_WINDOW_SITE in xevent-router-ximp.pkg");
raise exception DIE ""; # Should not get here.
};
esac;
};
fun get_''seen_first_expose''_oneshot_of (window_id: xt::Window_Id)
=
{ reply_oneshot = make_oneshot_maildrop ()
: Oneshot_Maildrop( Oneshot_Maildrop(Void) );
#
put_in_mailqueue (client_q,
#
\\ ({ me, imports, ... }: Runstate)
=
{ (get_winfo (me, window_id)) -> WINDOW_INFO { seen_first_expose_oneshot, ... };
#
put_in_oneshot (reply_oneshot, seen_first_expose_oneshot);
}
);
get_from_oneshot reply_oneshot;
};
fun get_''gui_startup_complete''_oneshot_of ()
=
{ reply_oneshot = make_oneshot_maildrop ()
: Oneshot_Maildrop( Oneshot_Maildrop(Void) );
#
put_in_mailqueue (client_q,
#
\\ ({ me, imports, ... }: Runstate)
=
put_in_oneshot (reply_oneshot, gui_startup_complete_oneshot)
);
get_from_oneshot reply_oneshot;
};
fun get_window_site (window_id: xt::Window_Id) # PUBLIC.
=
{ reply_oneshot = make_oneshot_maildrop ()
: Oneshot_Maildrop( g2d::Box );
#
put_in_mailqueue (client_q,
#
\\ ({ me, imports, ... }: Runstate)
=
{ (get_winfo (me, window_id)) -> WINDOW_INFO { site, ... };
#
put_in_oneshot (reply_oneshot, *site);
}
);
get_from_oneshot reply_oneshot;
};
fun given_window_id_pass_site # PUBLIC.
(window_id: xt::Window_Id)
(replyqueue: Replyqueue)
(reply_handler: g2d::Box -> Void)
=
{ reply_oneshot = make_oneshot_maildrop()
: Oneshot_Maildrop( g2d::Box );
#
put_in_mailqueue (client_q,
#
\\ ({ me, imports, ... }: Runstate)
=
{ (get_winfo (me, window_id)) -> WINDOW_INFO { site, ... };
#
put_in_oneshot (reply_oneshot, *site);
}
);
put_in_replyqueue (replyqueue, (get_from_oneshot' reply_oneshot) ==> reply_handler);
};
#
fun put_value (xevent: xet::x::Event) # PUBLIC.
=
put_in_mailqueue (xevent_q, xevent);
end;
fun process_options (options: List(Option), { name })
=
{ my_name = REF name;
#
apply do_option options
where
fun do_option (MICROTHREAD_NAME n) = my_name := n;
end;
{ name => *my_name };
};
##########################################################################################
# PUBLIC.
#
fun make_xevent_router_egg (options: List(Option)) # PUBLIC. PHASE 1: Construct our state and initialize from 'options'.
=
{ (process_options (options, { name => "tmp" }))
->
{ name };
me = { wid_to_winfo => REF xm::empty: Ref (xm::Map( Window_Info ) ), # "wid_to_winfo" == "window id to window info map"
#
# wid_to_pleas => REF xm::empty: Ref (xm::Map( List(Client_Plea) ) ), # "wid_to_pleas" == "window id to window pleas map"
wid_to_1shot => REF xm::empty: Ref (xm::Map( Oneshot_Maildrop(Void) ) ) # "wid_to_1shot" == "window id to oneshot map"
};
\\ () = { reply_oneshot = make_oneshot_maildrop(): Oneshot_Maildrop( (Me_Slot, Exports) ); # PUBLIC. PHASE 2: Start our microthread and return our Exports to caller.
#
xlogger::make_thread name (startup reply_oneshot); # Note that startup() is curried.
(get_from_oneshot reply_oneshot) -> (me_slot, exports);
fun phase3 # PUBLIC. PHASE 3: Accept our Imports, then wait for Run_Gun to fire.
( imports: Imports,
run_gun': Run_Gun,
end_gun': End_Gun
)
=
{
put_in_mailslot (me_slot, { me, imports, run_gun', end_gun' });
};
(exports, phase3);
};
};
}; # package xevent_router_ximp
end;