summaryrefslogtreecommitdiff
path: root/src/Gtk/Signal.icl
blob: be019b53d4725034879a95e5584e3f81c6a32155 (plain) (blame)
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
implementation module Gtk.Signal

import StdEnv
import StdMaybe
import StdDebug

import Control.Monad
import Data.Functor
import qualified Data.Map

import Gdk.Events

import Gtk
import Gtk.Internal

instance signalHandler GSignalHandler
where
	signalName h = case h of
		ActivateHandler _      -> "activate"
		ChangedHandler _       -> "changed"
		ClickedHandler _       -> "clicked"
		DeleteEventHandler _   -> "delete-event"
		DestroyHandler _       -> "destroy"
		KeyPressHandler _      -> "key-press-event"
		NextMatchHandler _     -> "next-match"
		PreviousMatchHandler _ -> "previous-match"
		SearchChangedHandler _ -> "search-changed"
		StopSearchHandler _    -> "stop-search"
	signalHandler h = case h of
		ActivateHandler f      -> SHI_Void f
		ChangedHandler f       -> SHI_Void f
		ClickedHandler f       -> SHI_Void f
		DeleteEventHandler f   -> SHI_Pointer_Bool \ev -> toBool <$> f (GdkEvent ev)
		DestroyHandler f       -> SHI_Void f
		KeyPressHandler f      -> SHI_Pointer_Bool \ev -> toBool <$> f (GdkEvent ev)
		NextMatchHandler f     -> SHI_Void f
		PreviousMatchHandler f -> SHI_Void f
		SearchChangedHandler f -> SHI_Void f
		StopSearchHandler f    -> SHI_Void f
	where
		toBool :: !GtkPropagate -> Bool
		toBool p = p=:StopPropagation

installSignalHandler :: !h !w -> GtkM w | signalHandler h & gtkWidget w
installSignalHandler handler widget =
	let handler_internal = signalHandler handler in
	modState (\st ->
		{ st
		& signal_handlers = 'Data.Map'.put (st.signal_counter+1) handler_internal st.signal_handlers
		, signal_counter  = st.signal_counter+1
		}) >>= \{signal_counter=id} ->
	let w = toPtr widget in
	toState (g_signal_connect w (signalName handler) (callback handler_internal) id) >>|
	pure widget
where
	callback handler = case handler of
		SHI_Void _                         -> callback_void
		SHI_Int_Int_Bool _                 -> callback_int_int_bool
		SHI_Pointer_Bool _                 -> callback_pointer_bool
		SHI_Int_Int_Pointer_Pointer_Bool _ -> callback_int_int_pointer_pointer_bool

	callback_void :: Pointer
	callback_void = code {
		pushLc handleSignal_void
	}
	callback_int_int_bool :: Pointer
	callback_int_int_bool = code {
		pushLc handleSignal_int_int_bool
	}
	callback_pointer_bool :: Pointer
	callback_pointer_bool = code {
		pushLc handleSignal_pointer_bool
	}
	callback_int_int_pointer_pointer_bool :: Pointer
	callback_int_int_pointer_pointer_bool = code {
		pushLc handleSignal_int_int_pointer_pointer_bool
	}

instance tune w GSignalHandler | gtkWidget w
where
	tune handler widget = installSignalHandler handler widget

addTimeout :: !GtkTimeout !(GtkM Bool) -> GtkM ()
addTimeout interval callback =
	modState (\st ->
		let id = st.timeout_counter+1 in
		{ st
		& timeouts        = 'Data.Map'.put id (wrapped_callback id) st.timeouts
		, timeout_counter = id
		}) >>= \{timeout_counter=id} ->
	case interval of
		Milliseconds ms -> toState (g_timeout_add ms get_handleTimeout_address id)
		Seconds s ->       toState (g_timeout_add_seconds s get_handleTimeout_address id)
where
	wrapped_callback id =
		callback >>= \r ->
		if r
			getState
			(modState \st -> {st & timeouts='Data.Map'.del id st.timeouts}) >>|
		pure r

	get_handleTimeout_address :: Pointer
	get_handleTimeout_address = code {
		pushLc handleTimeout
	}

withPossibleCallback :: !(GtkM a) -> GtkM a
withPossibleCallback m = saveState >>| m >>= \r -> retrieveState >>| pure r

// NB: low-level hacking to use and modify the GtkState from within callbacks.
// We use a CAF to keep track of the state. In runGtk, the state is saved with
// saveState. This state is retrieved with retrieveState there (to check
// whether the application should quit), but is also used here (in
// handleSignal) to be used and modified from signal callbacks.
saved_state :: {!GtkState}
saved_state =: {newGtkState}

save_state :: !GtkState !.a -> .a
save_state state env
	# saved_state = mk_unique saved_state
	  saved_state & [0] = state
	| saved_state.[0].return <> state.return
		= abort "internal error in saveState\n"
		= env
where
	mk_unique :: !{!GtkState} -> *{!GtkState}
	mk_unique _ = code {
		no_op
	}

saveState :: GtkM ()
saveState = getState >>= \state -> toState (save_state state)

retrieveState :: GtkM GtkState
retrieveState = modState (const saved_state.[0])

foreign export handleSignal_void
handleSignal_void :: !Pointer !Int -> Int
handleSignal_void _ id = handleSignal id \h -> case h of
	SHI_Void f
		-> Just (toInt <$> f)
		-> Nothing

foreign export handleSignal_pointer_bool
handleSignal_pointer_bool :: !Pointer !Pointer !Int -> Int
handleSignal_pointer_bool _ p id = handleSignal id \h -> case h of
	SHI_Pointer_Bool f
		-> Just (toInt <$> f p)
		-> Nothing

foreign export handleSignal_int_int_bool
handleSignal_int_int_bool :: !Pointer !Int !Int !Int -> Int
handleSignal_int_int_bool _ i1 i2 id = handleSignal id \h -> case h of
	SHI_Int_Int_Bool f
		-> Just (toInt <$> f i1 i2)
		-> Nothing

foreign export handleSignal_int_int_pointer_pointer_bool
handleSignal_int_int_pointer_pointer_bool :: !Pointer !Int !Int !Pointer !Pointer !Int -> Int
handleSignal_int_int_pointer_pointer_bool _ i1 i2 p1 p2 id = handleSignal id \h -> case h of
	SHI_Int_Int_Pointer_Pointer_Bool f
		-> Just (toInt <$> f i1 i2 p1 p2)
		-> Nothing

instance toInt () where toInt () = 0
instance toInt Bool where toInt b = if b 1 0

handleSignal :: !Int !(SignalHandlerInternal -> Maybe (GtkM Int)) -> Int
handleSignal id handle
	# st = saved_state.[0]
	= case 'Data.Map'.get id st.signal_handlers of
		Nothing
			-> trace_n ("handleSignal: missing signal handler #"+++toString id) 0
		Just handler
			-> case handle handler of
				Nothing
					-> trace_n "handleSignal: signal handler does not match" 0
				Just (GtkM f)
					# (r,st) = f st
					-> save_state st r

foreign export handleTimeout
handleTimeout :: !Int -> Int
handleTimeout id
	# st = saved_state.[0]
	= case 'Data.Map'.get id st.timeouts of
		Nothing
			-> trace_n ("handleTimeout: missing function #"+++toString id) 0
		Just (GtkM f)
			# (b,st) = f st
			-> save_state st (if b 1 0)