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