1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
############################################################################
#
# File: evmux.icn
#
# Subject: Procedures for window event multiplexor
#
# Author: Gregg M. Townsend
#
# Date: August 14, 1996
#
############################################################################
#
# This file is in the public domain.
#
############################################################################
#
# These procedures implement a simple event handling package. This
# package has more recently been superseded by the vidget library.
#
# The event multiplexor is configured by registering *sensors*, which
# respond to events that occur when the mouse cursor is within a
# particular region. When a sensor fires, it calls a user procedure
# that was registered when the sensor was created.
#
# These routines interpret window events and invoke callbacks:
#
# sensor() registers the events of interest.
#
# evhandle() reads and responds to the next event.
#
# evmux() loops forever, handling events.
#
# Two other small procedures help build event-driven programs:
#
# quitsensor() registers a standardized response to Q or q.
#
# argless() is a "glue" procedure usable as a callback.
#
############################################################################
#
# sensor(win, ev, proc, arg, x, y, w, h) -- register an event responder.
#
# registers *proc* as the procedure to be called when the event[s]
# *ev* occur within the given bounds inside window *win* and returns
# a handle. The default bounds encompass the entire window.
#
# The event set *ev* can be either:
# -- a cset or string specifying particular keypresses of interest
# -- one of the event keywords (&lpress, &rdrag, &resize, etc.)
#
# When a matching event occurs, proc(win, arg, x, y, e) is called. proc,
# win, and arg are as recorded from the sensor call. x and y give the
# current mouse position and e the event; for a keypress, this is the
# character.
#
# No event generates more than one procedure call.
# In the case of conflicting entries, the later registrant wins.
#
# delsensor(win, x) deletes sensor x from the specified window.
# If x is null, all sensors are deleted.
#
#
# evmux(win) -- loop forever, calling event handlers as appropriate.
# evhandle(win) -- wait for the next event, and handle it.
#
# evmux(win) is an infinite loop that calls user routines in response
# to window events. It is for programs that don't need to do other
# work while waiting for window input.
#
# evhandle(win) processes one event and then returns to its caller,
# allowing external loop control. evhandle returns the outcome of
# the handler proc, or fails if there is no handler for the event.
#
# quitsensor(win, wait) -- standardized "quit" sensor
#
# quitsensor() registers a sensor that calls exit() when either
# "q" or "Q" is typed in the window.
#
# If wait is non-null, quitsensor does not return but just waits for
# the signal (useful in non-interactive display programs).
#
#
# argless(win, proc) -- call proc with no arguments.
#
# Useful for registering argless procedures as in quitsensor() above.
#
############################################################################
#
# Requires: Version 9 graphics
#
############################################################################
#
# See also: button.icn, slider.icn
#
############################################################################
record EvMux_Rec(ev, proc, arg, x, y, w, h)
global EvMux_Windows
## sensor(win, ev, proc, arg, x, y, w, h) -- register an event responder.
procedure sensor(win, ev, proc, arg, x, y, w, h)
local evlist, r, e
/EvMux_Windows := table()
/EvMux_Windows[win] := list()
evlist := EvMux_Windows[win]
/x := -WAttrib(win, "dx")
/y := -WAttrib(win, "dy")
/w := WAttrib(win, "width") - (x + WAttrib(win, "dx"))
/h := WAttrib(win, "height") - (y + WAttrib(win, "dy"))
if w < 0 then
x -:= (w := -w)
if h < 0 then
y -:= (h := -h)
if type(ev) == ("cset" | "string") then
ev := cset(ev)
else
ev := cset(evchar(ev)) | stop("invalid event specification: ", image(ev))
push(evlist, r := EvMux_Rec(ev, proc, arg, x, y, w, h))
return r
end
## delsensor(win, x) -- delete sensor x, or all sensors, from window.
procedure delsensor(win, x)
local t
t := \EvMux_Windows[win] | fail
if /x then {
delete(EvMux_Windows, win) # delete whole set of sensors
return
}
if not (x === !t) then
fail # not registered in this window
# Sensor is registered for this window. Disable it.
x.ev := ''
# Remove disabled sensors from list, if possible.
while *t[1].ev = 0 do
pop(t)
while *t[-1].ev = 0 do
pull(t)
# If nothing is left on list, delete from table.
if *t = 0 then
delete(EvMux_Windows, win)
return
end
## evchar(e) -- map mouse event to character code.
#
# Internally, *all* events are single-character strings, and mouse & resizing
# events are mapped into characters that are never returned as keypress events.
procedure evchar(s)
return case s of {
&lpress: "\237" # mouse button 1 down
&mpress: "\236" # mouse button 2 down
&rpress: "\235" # mouse button 3 down
&lrelease: "\234" # mouse button 1 up
&mrelease: "\233" # mouse button 2 up
&rrelease: "\232" # mouse button 3 up
&ldrag: "\231" # mouse button 1 is dragging
&mdrag: "\230" # mouse button 2 is dragging
&rdrag: "\227" # mouse button 3 is dragging
&resize: "\226" # window has resized
}
fail
end
## evmux(win) -- loop forever, calling event handlers as appropriate.
## evhandle(win) -- wait for the next event, and handle it.
# produce result of the handler proc; fail if nobody handles.
procedure evmux(win)
repeat
evhandle(win)
end
procedure evhandle(win)
local x, y, ev, e, r, t
t := (\EvMux_Windows)[win] | stop("no events registered for window")
ev := Event(win)
x := &x
y := &y
# convert event code to single character
if type(ev) == "integer" then
e := evchar(ev) | ""
else
e := ev
# find and call the first (most recent) matching handler
# (just a simple serial search)
every r := !t do
if any(r.ev, e) & ontarget(r, x, y) then
return r.proc(win, r.arg, x, y, ev)
fail
end
## ontarget(r, x, y) -- check if an event is within bounds
#
# checks that (x, y) are within the bounds of (r.x, r.y, r.w, r.h).
procedure ontarget(r, x, y)
return (x -:= r.x) >= 0 & x < r.w & (y -:= r.y) >= 0 & y < r.h
end
## quitsensor(win, wait) -- standardized "quit" sensor
procedure quitsensor(win, wait)
sensor(win, 'qQ', argless, exit)
if \wait then evmux(win)
return
end
## argless(win, proc) -- call proc with no arguments.
procedure argless(win, proc)
return proc()
end
|