1 /* SCCS Id: @(#)gnsignal.c 3.4 2000/07/16 */
2 /* Copyright (C) 1998 by Anthony Taylor <tonyt@ptialaska.net> */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include "gnsignal.h"
6 #include "gnmain.h"
7 #include <gdk/gdkkeysyms.h>
8
9 GList *g_keyBuffer;
10 GList *g_clickBuffer;
11 int g_numKeys=0;
12 int g_numClicks=0;
13 int g_askingQuestion=0;
14 static int s_done=FALSE;
15
16 /*
17 * ghack_init_signals
18 *
19 * Create some signals and attach them to the GtkWidget class.
20 * These are the signals:
21 *
22 * ghack_curs : NONE:INT,INT
23 * INT 1 = x
24 * INT 2 = y
25 *
26 * ghack_putstr : NONE:INT,POINTER
27 * INT = attribute
28 * POINTER = char* string to print
29 *
30 * ghack_print_glyph : NONE:INT,INT,POINTER
31 * INT 1 = x
32 * INT 2 = y
33 * INT 3 = GtkPixmap* to rendered glyph
34 *
35 * ghack_clear : NONE:NONE
36 *
37 * ghack_display : NONE:BOOL
38 * BOOL = blocking flag
39 *
40 * ghack_start_menu : NONE:NONE
41 *
42 * ghack_add_menu : NONE:POINTER
43 * POINTER = GHackMenuItem*
44 *
45 * ghack_end_menu : NONE:POINTER
46 * POINTER = char* to closing string
47 *
48 * ghack_select_menu : NONE:POINTER,INT,POINTER
49 * POINTER = int pointer-- filled with number
50 * of selected items on return
51 * INT = number of items selected
52 * POINTER = structure to fill
53 *
54 * ghack_cliparound : NONE:INT,INT
55 * INT 1 = x
56 * INT 2 = y
57 */
58
59 void
ghack_init_signals(void)60 ghack_init_signals( void)
61 {
62 ghack_signals[GHSIG_CURS] =
63 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
64 "ghack_curs",
65 GTK_RUN_FIRST,
66 gtk_marshal_NONE__INT_INT,
67 GTK_TYPE_NONE,
68 2,
69 GTK_TYPE_INT,
70 GTK_TYPE_INT);
71
72 ghack_signals[GHSIG_PUTSTR] =
73 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
74 "ghack_putstr",
75 GTK_RUN_FIRST,
76 gtk_marshal_NONE__INT_POINTER,
77 GTK_TYPE_NONE,
78 2,
79 GTK_TYPE_INT,
80 GTK_TYPE_POINTER);
81
82 ghack_signals[GHSIG_PRINT_GLYPH] =
83 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
84 "ghack_print_glyph",
85 GTK_RUN_FIRST,
86 gtk_marshal_NONE__INT_INT_POINTER,
87 GTK_TYPE_NONE,
88 3,
89 GTK_TYPE_INT,
90 GTK_TYPE_INT,
91 GTK_TYPE_POINTER);
92
93 ghack_signals[GHSIG_CLEAR] =
94 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
95 "ghack_clear",
96 GTK_RUN_FIRST,
97 gtk_marshal_NONE__NONE,
98 GTK_TYPE_NONE,
99 0);
100
101 ghack_signals[GHSIG_DISPLAY] =
102 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
103 "ghack_display",
104 GTK_RUN_FIRST,
105 gtk_marshal_NONE__BOOL,
106 GTK_TYPE_NONE,
107 1,
108 GTK_TYPE_BOOL);
109
110 ghack_signals[GHSIG_START_MENU] =
111 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
112 "ghack_start_menu",
113 GTK_RUN_FIRST,
114 gtk_marshal_NONE__NONE,
115 GTK_TYPE_NONE,
116 0);
117
118 ghack_signals[GHSIG_ADD_MENU] =
119 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
120 "ghack_add_menu",
121 GTK_RUN_FIRST,
122 gtk_marshal_NONE__POINTER,
123 GTK_TYPE_NONE,
124 1,
125 GTK_TYPE_POINTER);
126
127 ghack_signals[GHSIG_END_MENU] =
128 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
129 "ghack_end_menu",
130 GTK_RUN_FIRST,
131 gtk_marshal_NONE__POINTER,
132 GTK_TYPE_NONE,
133 1,
134 GTK_TYPE_POINTER);
135
136 ghack_signals[GHSIG_SELECT_MENU] =
137 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
138 "ghack_select_menu",
139 GTK_RUN_FIRST,
140 gtk_marshal_NONE__POINTER_INT_POINTER,
141 GTK_TYPE_NONE,
142 3,
143 GTK_TYPE_POINTER,
144 GTK_TYPE_INT,
145 GTK_TYPE_POINTER);
146
147 ghack_signals[GHSIG_CLIPAROUND] =
148 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
149 "ghack_cliparound",
150 GTK_RUN_FIRST,
151 gtk_marshal_NONE__INT_INT,
152 GTK_TYPE_NONE,
153 2,
154 GTK_TYPE_INT,
155 GTK_TYPE_INT);
156
157 ghack_signals[GHSIG_FADE_HIGHLIGHT] =
158 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
159 "ghack_fade_highlight",
160 GTK_RUN_FIRST,
161 gtk_marshal_NONE__NONE,
162 GTK_TYPE_NONE,
163 0);
164
165 ghack_signals[GHSIG_DELAY] =
166 gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()),
167 "gnome_delay_output",
168 GTK_RUN_FIRST,
169 gtk_marshal_NONE__INT,
170 GTK_TYPE_NONE,
171 1,
172 GTK_TYPE_INT);
173
174 }
175
176 /* For want of a better place, I'm putting the delay output stuff here
177 * -Erik
178 */
timeout_callback(gpointer data)179 static gint timeout_callback(gpointer data)
180 {
181 s_done=TRUE;
182 return FALSE;
183 }
184
185 void
ghack_delay(GtkWidget * win,int numMillisecs,gpointer data)186 ghack_delay( GtkWidget *win, int numMillisecs, gpointer data)
187 {
188 s_done=FALSE;
189 gtk_timeout_add( (unsigned int) numMillisecs,
190 timeout_callback, NULL);
191 while( s_done==FALSE)
192 gtk_main_iteration();
193 }
194
195
196 void
ghack_handle_button_press(GtkWidget * widget,GdkEventButton * event,gpointer data)197 ghack_handle_button_press(GtkWidget *widget, GdkEventButton *event,
198 gpointer data)
199 {
200 GHClick *click;
201 double x1, y1;
202
203 if (event->type != GDK_BUTTON_PRESS)
204 return;
205
206 gnome_canvas_window_to_world( GNOME_CANVAS( widget), event->x,
207 event->y, &x1, &y1);
208 /*
209 g_message("I got a click at %f,%f with button %d \n",
210 x1, y1, event->button);
211 */
212
213 /* We allocate storage here, so we need to remember if (g_numClicks>0)
214 * to blow this away when closing the app using something like
215 * while (g_clickBuffer)
216 * {
217 * g_free((GHClick)g_clickBuffer->data);
218 * g_clickBuffer = g_clickBuffer->next;
219 * }
220 * g_list_free( g_clickBuffer );
221 *
222 */
223 click = g_new( GHClick, 1);
224
225 click->x=(int)x1/ghack_glyph_width();
226 click->y=(int)y1/ghack_glyph_height();
227 click->mod=(event->button == 1)? CLICK_1 : CLICK_2;
228
229 g_clickBuffer = g_list_prepend (g_clickBuffer, click);
230 /* Could use g_list_length(), but it is stupid and just
231 * traverses the list while counting, so we'll just do
232 * the counting ourselves in advance. */
233 g_numClicks++;
234 }
235
236 #ifndef M
237 # ifndef NHSTDC
238 # define M(c) (0x80 | (c))
239 # else
240 # define M(c) ((c) - 128)
241 # endif /* NHSTDC */
242 #endif
243 #ifndef C
244 #define C(c) (0x1f & (c))
245 #endif
246
247
248 void
ghack_handle_key_press(GtkWidget * widget,GdkEventKey * event,gpointer data)249 ghack_handle_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
250 {
251 static int was_pound = 0;
252 int key = 0;
253 int ctl = GDK_CONTROL_MASK;
254 int alt = GDK_MOD1_MASK;
255
256 /* Turn this on to debug key events */
257 #if 0
258 g_message("I got a \"%s\" key (%d) %s%s",
259 gdk_keyval_name (event->keyval), event->keyval,
260 (event->state&ctl)? "+CONTROL":"", (event->state&alt)? "+ALT":"");
261 #endif
262
263 switch (event->keyval) {
264 /* special keys to do stuff with */
265
266 /* Set up the direction keys */
267
268 /* First handle the arrow keys -- these always mean move */
269 case GDK_Right:
270 case GDK_rightarrow:
271 if (iflags.num_pad) key='6'; else key='l'; break;
272 case GDK_Left:
273 case GDK_leftarrow:
274 if (iflags.num_pad) key='4'; else key='h'; break;
275 case GDK_Up:
276 case GDK_uparrow:
277 if (iflags.num_pad) key='8'; else key='k'; break;
278 case GDK_Down:
279 case GDK_downarrow:
280 if (iflags.num_pad) key='2'; else key='j'; break;
281 case GDK_Home:
282 if (iflags.num_pad) key='7'; else key='y'; break;
283 case GDK_End:
284 if (iflags.num_pad) key='1'; else key='b'; break;
285 case GDK_Page_Down:
286 if (iflags.num_pad) key='3'; else key='n'; break;
287 case GDK_Page_Up:
288 if (iflags.num_pad) key='9'; else key='u'; break;
289 case ' ': key='.'; break;
290
291 /* Now, handle the numberpad (move or numbers) */
292 case GDK_KP_Right:
293 case GDK_KP_6:
294 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
295 key = GDK_KP_6;
296 else
297 key='6';
298 break;
299
300 case GDK_KP_Left:
301 case GDK_KP_4:
302 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
303 key = GDK_KP_4;
304 else
305 key='4';
306 break;
307
308 case GDK_KP_Up:
309 case GDK_KP_8:
310 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
311 key = GDK_KP_8;
312 else
313 key='8';
314 break;
315
316 case GDK_KP_Down:
317 case GDK_KP_2:
318 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
319 key = GDK_KP_2;
320 else
321 key='2';
322 break;
323
324 /* Move Top-Left */
325 case GDK_KP_Home:
326 case GDK_KP_7:
327 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
328 key = GDK_KP_7;
329 else
330 key='7';
331 break;
332
333 case GDK_KP_Page_Up:
334 case GDK_KP_9:
335 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
336 key = GDK_KP_9;
337 else
338 key='9';
339 break;
340
341 case GDK_KP_End:
342 case GDK_KP_1:
343 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
344 key = GDK_KP_1;
345 else
346 key='1';
347 break;
348
349 case GDK_KP_Page_Down:
350 case GDK_KP_3:
351 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
352 key = GDK_KP_3;
353 else
354 key='3';
355 break;
356
357
358 case GDK_KP_5:
359 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad)
360 key = GDK_KP_5;
361 else
362 key='5';
363 break;
364
365 case GDK_KP_Delete:
366 case GDK_KP_Decimal:
367 key='.';
368 break;
369
370 /* can't just ignore "#", it's a core feature */
371 case GDK_numbersign:
372 key='#';
373 break;
374
375 /* We will probably want to do something with these later... */
376 case GDK_KP_Begin:
377 case GDK_KP_F1:
378 case GDK_F1:
379 case GDK_KP_F2:
380 case GDK_F2:
381 case GDK_KP_F3:
382 case GDK_F3:
383 case GDK_KP_F4:
384 case GDK_F4:
385 case GDK_F5:
386 case GDK_F6:
387 case GDK_F7:
388 case GDK_F8:
389 case GDK_F9:
390 case GDK_F10:
391 case GDK_F11:
392 case GDK_F12:
393 break;
394 /* various keys to ignore */
395 case GDK_KP_Insert:
396 case GDK_Insert:
397 case GDK_Delete:
398 case GDK_Print:
399 case GDK_BackSpace:
400 case GDK_Pause:
401 case GDK_Scroll_Lock:
402 case GDK_Shift_Lock:
403 case GDK_Num_Lock:
404 case GDK_Caps_Lock:
405 case GDK_Control_L:
406 case GDK_Control_R:
407 case GDK_Shift_L:
408 case GDK_Shift_R:
409 case GDK_Alt_L:
410 case GDK_Alt_R:
411 case GDK_Meta_L:
412 case GDK_Meta_R:
413 case GDK_Mode_switch:
414 case GDK_Multi_key:
415 return;
416
417 default:
418 key = event->keyval;
419 break;
420 }
421
422 if ((event->state & alt) || was_pound) {
423 key=M(event->keyval);
424 } else if (event->state & ctl) {
425 key=C(event->keyval);
426 }
427 if (was_pound) {
428 was_pound = 0;
429 }
430
431 /* Ok, here is where we do clever stuff to overide the default
432 * game behavior */
433 if (g_askingQuestion == 0) {
434
435 if (key == 'S' || key == M('S') || key == C('S')) {
436 ghack_save_game_cb( NULL, NULL);
437 return;
438 }
439 }
440 g_keyBuffer = g_list_prepend (g_keyBuffer, GINT_TO_POINTER( key));
441 g_numKeys++;
442 }
443