## ro-pixmap-ximp.pkg
#
# Support for icons, button images
# and so forth: Track what readonly
# windows we have on the X server and
# transparently load new ones as needed.
#
# Compiled by:
#
src/lib/x-kit/widget/xkit-widget.sublib### "Opportunity is missed by most people
### because it is dressed in overalls
### and looks like work."
###
### -- Thomas Edison
stipulate
include package threadkit; # threadkit is from
src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg #
package bio = bitmap_io; # bitmap_io is from
src/lib/x-kit/draw/bitmap-io.pkg package fil = file__premicrothread; # file__premicrothread is from
src/lib/std/src/posix/file--premicrothread.pkg package qk = quark; # quark is from
src/lib/x-kit/style/quark.pkg package xc = xclient; # xclient is from
src/lib/x-kit/xclient/xclient.pkg package rpp = ro_pixmap_port; # ro_pixmap_port is from
src/lib/x-kit/widget/lib/ro-pixmap-port.pkg package cpm = cs_pixmap; # cs_pixmap is from
src/lib/x-kit/xclient/src/window/cs-pixmap.pkg package rpm = ro_pixmap; # ro_pixmap is from
src/lib/x-kit/xclient/src/window/ro-pixmap.pkgherein
package ro_pixmap_ximp
: (weak) Ro_Pixmap_Ximp # Ro_Pixmap_Ximp is from
src/lib/x-kit/widget/lib/ro-pixmap-ximp.api {
Exports = { # Ports we export for use by other imps.
ro_pixmap_port: rpp::Ro_Pixmap_Port # Requests from widget/application code.
};
Imports = { # Ports we use which are exported by other imps.
name_to_cs_pixmap: qk::Quark -> cpm::Cs_Pixmap
};
Option = MICROTHREAD_NAME String; #
Ro_Pixmap_Egg = Void -> (Exports, (Imports, Run_Gun, End_Gun) -> Void);
exception BAD_NAME;
package qht
=
typelocked_hashtable_g (
#
Hash_Key = qk::Quark;
same_key = qk::same;
hash_value = qk::hash;
);
Window_Table
=
qht::Hashtable( rpm::Ro_Pixmap );
Ro_Pixmap_Ximp_State # Holds all mutable state maintained by ximp.
=
{
window_table: Window_Table
};
Me_Slot = Mailslot( { imports: Imports,
me: Ro_Pixmap_Ximp_State,
run_gun': Run_Gun,
end_gun': End_Gun,
screen: xsession_junk::Screen,
name_to_cs_pixmap: qk::Quark -> cpm::Cs_Pixmap
}
);
# typelocked_hashtable_g is from
src/lib/src/typelocked-hashtable-g.pkg exception NOT_FOUND;
Runstate = { # These values will be statically globally visible throughout the code body for the imp.
me: Ro_Pixmap_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.
screen: xsession_junk::Screen,
name_to_cs_pixmap: qk::Quark -> cpm::Cs_Pixmap
};
Client_Q = Mailqueue( Runstate -> Void );
fun run ( client_q: Client_Q, # Requests from x-widgets and such via draw_imp, pen_imp or font_imp.
#
runstate as
{ # These values will be statically globally visible throughout the code body for the imp.
me: Ro_Pixmap_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.
screen: xsession_junk::Screen,
name_to_cs_pixmap: qk::Quark -> cpm::Cs_Pixmap
}
)
=
loop ()
where
fun loop () # Outer loop for the imp.
=
{ do_one_mailop' to [
#
end_gun' ==> shut_down_ro_pixmap_ximp',
take_from_mailqueue' client_q ==> do_client_plea
];
loop ();
}
where
fun do_client_plea thunk
=
thunk runstate;
fun shut_down_ro_pixmap_ximp' ()
=
thread_exit { success => TRUE }; # Will not return.
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;
#
ro_pixmap_port = {
get_ro_pixmap
};
to = make_replyqueue ();
put_in_oneshot (reply_oneshot, (me_slot, { ro_pixmap_port })); # Return value from ro_pixmap_egg'().
(take_from_mailslot me_slot) # Imports from ro_pixmap_egg'().
->
{ me, imports, run_gun', end_gun', screen, name_to_cs_pixmap };
block_until_mailop_fires run_gun'; # Wait for the starting gun.
run (client_q, { me, imports, to, end_gun', screen, name_to_cs_pixmap }); # Will not return.
}
where
client_q = make_mailqueue (get_current_microthread()) : Client_Q;
fun get_ro_pixmap (name: String)
=
{ reply_1shot = make_oneshot_maildrop ();
#
put_in_mailqueue (client_q,
#
\\ ({ me, screen, name_to_cs_pixmap, ... }: Runstate)
=
{ quark = qk::quark name;
#
result = case (find_window quark)
#
NULL => make_window (name, quark);
s => s;
esac;
put_in_oneshot (reply_1shot, result);
}
where
note_window = qht::set me.window_table;
find_window = qht::find me.window_table;
# Parse file, being careful to close
# it properly if an exception is raised:
#
fun parse_file (fd, parse)
=
(parse fd
then
fil::close_input fd
)
except
e = { fil::close_input fd;
raise exception e;
};
fun make_window_from_file (name, quark)
=
{ file_name = substring (name, 1, size name - 1);
#
fd = fil::open_for_read file_name;
(parse_file (fd, bio::read_bitmap))
->
{ image, ... };
t = rpm::make_readonly_pixmap_from_clientside_pixmap
screen
image;
note_window (quark, t);
THE t;
};
fun make_window_from_clientside_pixmap quark
=
{ window = rpm::make_readonly_pixmap_from_clientside_pixmap
screen
(name_to_cs_pixmap quark);
note_window (quark, window);
THE window;
};
fun make_window (arg as (name, q))
=
if (string::get_byte_as_char (name, 0) == '@')
#
make_window_from_file arg;
else
make_window_from_clientside_pixmap q;
fi
except _ = NULL;
end
);
get_from_oneshot reply_1shot;
};
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_ro_pixmap_egg # PUBLIC. PHASE 1: Construct our state and initialize from 'options'.
(
screen: xsession_junk::Screen,
options: List(Option)
)
=
{ (process_options (options, { name => "ro_pixmap" }))
->
{ name };
me = {
window_table => qht::make_hashtable { size_hint => 32, not_found_exception => NOT_FOUND }
};
\\ () = { 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
)
=
{ name_to_cs_pixmap = imports.name_to_cs_pixmap;
#
put_in_mailslot (me_slot, { me, imports, run_gun', end_gun', screen, name_to_cs_pixmap });
};
(exports, phase3);
};
};
};
end;