1 /*
2  *    TTTTTTTTTTTTTT  EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
3  *    TTTTTTTTTTTTTT  EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
4  *          TT        EE              OO          OO
5  *          TT        EE              OO          OO
6  *          TT        EE              OO          OO
7  *          TT        EEEEEEEEEE      OO          OO
8  *          TT        EEEEEEEEEE      OO          OO
9  *          TT        EE              OO          OO
10  *          TT        EE              OO          OO
11  *          TT        EE              OO          OO
12  *          TT        EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
13  *          TT        EEEEEEEEEEEEEE  OOOOOOOOOOOOOO
14  *
15  *                  L'�mulateur Thomson TO8
16  *
17  *  Copyright (C) 1997-2017 Gilles F�tis, Eric Botcazou, Alexandre Pukall,
18  *                          J�r�mie Guillaume, Fran�ois Mouret
19  *
20  *  This program is free software; you can redistribute it and/or modify
21  *  it under the terms of the GNU General Public License as published by
22  *  the Free Software Foundation; either version 2 of the License, or
23  *  (at your option) any later version.
24  *
25  *  This program is distributed in the hope that it will be useful,
26  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
27  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  *  GNU General Public License for more details.
29  *
30  *  You should have received a copy of the GNU General Public License
31  *  along with this program; if not, write to the Free Software
32  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
33  */
34 
35 /*
36  *  Module     : linux/udisplay.c
37  *  Version    : 1.8.4
38  *  Cr�� par   : Eric Botcazou octobre 1999
39  *  Modifi� par: Eric Botcazou 24/11/2003
40  *               Fran�ois Mouret 26/01/2010 08/2011 02/06/2012 28/12/2012
41  *                               23/08/2015 31/07/2016
42  *               Gilles F�tis 07/2011
43  *
44  *  Module d'interface avec le serveur X.
45  */
46 
47 
48 #ifndef SCAN_DEPEND
49    #include <stdio.h>
50    #include <stdlib.h>
51    #include <gtk/gtk.h>
52    #include <gtk/gtkx.h>
53    #include <gdk/gdkx.h>
54    #include <X11/Xlib.h>
55    #include <X11/XKBlib.h>
56    #include <X11/Xutil.h>
57    #include <X11/keysym.h>
58 #endif
59 
60 #include "defs.h"
61 #include "teo.h"
62 #include "to8keys.h"
63 #include "media/keyboard.h"
64 #include "media/mouse.h"
65 #include "media/disk.h"
66 #include "linux/gui.h"
67 #include "linux/display.h"
68 #include "linux/graphic.h"
69 
70 
71 GtkWidget *wMain;
72 GdkWindow *gwindow_win;
73 Display *display;
74 int screen;
75 int mit_shm_enabled;
76 Window screen_win;
77 Window window_win;
78 
79 static int need_modifiers_reset = TRUE;
80 
81 static int installed_pointer = TEO_STATUS_MOUSE;
82 
83 static int x11_to_dos[256];
84 
85 #define KB_SIZE  101
86 static struct {
87     int keysym;
88     int keycode;
89 } keyconv[KB_SIZE]={
90     { XK_Escape          , TEO_KEY_ESC        },
91     { XK_1               , TEO_KEY_1          },
92     { XK_2               , TEO_KEY_2          },
93     { XK_3               , TEO_KEY_3          },
94     { XK_4               , TEO_KEY_4          },
95     { XK_5               , TEO_KEY_5          },
96     { XK_6               , TEO_KEY_6          },
97     { XK_7               , TEO_KEY_7          },
98     { XK_8               , TEO_KEY_8          },
99     { XK_9               , TEO_KEY_9          },
100     { XK_0               , TEO_KEY_0          },
101     { XK_parenright      , TEO_KEY_MINUS      },
102     { XK_equal           , TEO_KEY_EQUALS     },
103     { XK_BackSpace       , TEO_KEY_BACKSPACE  },
104     { XK_Tab             , TEO_KEY_TAB        },
105     { XK_A               , TEO_KEY_Q          },
106     { XK_Z               , TEO_KEY_W          },
107     { XK_E               , TEO_KEY_E          },
108     { XK_R               , TEO_KEY_R          },
109     { XK_T               , TEO_KEY_T          },
110     { XK_Y               , TEO_KEY_Y          },
111     { XK_U               , TEO_KEY_U          },
112     { XK_I               , TEO_KEY_I          },
113     { XK_O               , TEO_KEY_O          },
114     { XK_P               , TEO_KEY_P          },
115     { XK_dead_circumflex , TEO_KEY_OPENBRACE  },
116     { XK_dollar          , TEO_KEY_CLOSEBRACE },
117     { XK_Return          , TEO_KEY_ENTER      },
118     { XK_Control_L       , TEO_KEY_LCONTROL   },
119     { XK_Q               , TEO_KEY_A          },
120     { XK_S               , TEO_KEY_S          },
121     { XK_D               , TEO_KEY_D          },
122     { XK_F               , TEO_KEY_F          },
123     { XK_G               , TEO_KEY_G          },
124     { XK_H               , TEO_KEY_H          },
125     { XK_J               , TEO_KEY_J          },
126     { XK_K               , TEO_KEY_K          },
127     { XK_L               , TEO_KEY_L          },
128     { XK_M               , TEO_KEY_COLON      },
129     { XK_percent         , TEO_KEY_QUOTE      },
130     { XK_twosuperior     , TEO_KEY_TILDE      },
131     { XK_Shift_L         , TEO_KEY_LSHIFT     },
132     { XK_asterisk        , TEO_KEY_ASTERISK   },
133     { XK_W               , TEO_KEY_Z          },
134     { XK_X               , TEO_KEY_X          },
135     { XK_C               , TEO_KEY_C          },
136     { XK_V               , TEO_KEY_V          },
137     { XK_B               , TEO_KEY_B          },
138     { XK_N               , TEO_KEY_N          },
139     { XK_comma           , TEO_KEY_M          },
140     { XK_semicolon       , TEO_KEY_COMMA      },
141     { XK_colon           , TEO_KEY_STOP       },
142     { XK_exclam          , TEO_KEY_SLASH      },
143     { XK_Shift_R         , TEO_KEY_RSHIFT     },
144     { XK_KP_Multiply     , TEO_KEY_ASTERISK   },
145     { XK_Alt_L           , TEO_KEY_ALT        },
146     { XK_space           , TEO_KEY_SPACE      },
147     { XK_Caps_Lock       , TEO_KEY_CAPSLOCK   },
148     { XK_F1              , TEO_KEY_F1         },
149     { XK_F2              , TEO_KEY_F2         },
150     { XK_F3              , TEO_KEY_F3         },
151     { XK_F4              , TEO_KEY_F4         },
152     { XK_F5              , TEO_KEY_F5         },
153     { XK_F6              , TEO_KEY_F6         },
154     { XK_F7              , TEO_KEY_F7         },
155     { XK_F8              , TEO_KEY_F8         },
156     { XK_F9              , TEO_KEY_F9         },
157     { XK_F10             , TEO_KEY_F10        },
158     { XK_Num_Lock        , TEO_KEY_NUMLOCK    },
159     { XK_Scroll_Lock     , TEO_KEY_SCRLOCK    },
160     { XK_KP_7            , TEO_KEY_7_PAD      },
161     { XK_KP_8            , TEO_KEY_8_PAD      },
162     { XK_KP_9            , TEO_KEY_9_PAD      },
163     { XK_KP_Subtract     , TEO_KEY_MINUS_PAD  },
164     { XK_KP_4            , TEO_KEY_4_PAD      },
165     { XK_KP_5            , TEO_KEY_5_PAD      },
166     { XK_KP_6            , TEO_KEY_6_PAD      },
167     { XK_KP_Add          , TEO_KEY_PLUS_PAD   },
168     { XK_KP_1            , TEO_KEY_1_PAD      },
169     { XK_KP_2            , TEO_KEY_2_PAD      },
170     { XK_KP_3            , TEO_KEY_3_PAD      },
171     { XK_KP_0            , TEO_KEY_0_PAD      },
172     { XK_KP_Decimal      , TEO_KEY_DEL_PAD    },
173     { XK_less            , TEO_KEY_BACKSLASH2 },
174     { XK_F11             , TEO_KEY_F11        },
175     { XK_F12             , TEO_KEY_F12        },
176     { XK_KP_Enter        , TEO_KEY_ENTER_PAD  },
177     { XK_Control_R       , TEO_KEY_RCONTROL   },
178     { XK_KP_Divide       , TEO_KEY_SLASH_PAD  },
179     { XK_ISO_Level3_Shift, TEO_KEY_ALTGR      },
180     { XK_Home            , TEO_KEY_HOME       },
181     { XK_Up              , TEO_KEY_UP         },
182     { XK_Page_Up         , TEO_KEY_PGUP       },
183     { XK_Left            , TEO_KEY_LEFT       },
184     { XK_Right           , TEO_KEY_RIGHT      },
185     { XK_End             , TEO_KEY_END        },
186     { XK_Down            , TEO_KEY_DOWN       },
187     { XK_Page_Down       , TEO_KEY_PGDN       },
188     { XK_Insert          , TEO_KEY_INSERT     },
189     { XK_Delete          , TEO_KEY_DEL        }
190 };
191 
192 
193 
194 /* SetPointer:
195  *  S�lectionne le pointeur actif.
196  */
SetPointer(int pointer)197 static void SetPointer(int pointer)
198 {
199     switch (pointer)
200     {
201         case TEO_STATUS_MOUSE :
202             gdk_window_set_cursor (gwindow_win, NULL);
203             installed_pointer=TEO_STATUS_MOUSE;
204             break;
205 
206         case TEO_STATUS_LIGHTPEN :
207             gdk_window_set_cursor (gwindow_win, gdk_cursor_new_for_display (gdk_window_get_display (gwindow_win), GDK_PENCIL));
208             installed_pointer=TEO_STATUS_LIGHTPEN;
209             break;
210     }
211 }
212 
213 
214 /* button_release_event:
215  *  Gestion des touches enfonc�es.
216  */
217 static gboolean
delete_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)218 delete_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
219 {
220     teo.command = TEO_COMMAND_QUIT;
221 
222     return TRUE;
223     (void)widget;
224     (void)event;
225     (void)user_data;
226 }
227 
228 
229 /* key_press_event:
230  *  Gestion des touches enfonc�es.
231  */
232 static gboolean
key_press_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)233 key_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
234 {
235     int value = 0;
236     int teo_key = 0;
237 
238     if (need_modifiers_reset)
239     {
240         if (event->key.state & GDK_SHIFT_MASK)   value |= TEO_KEY_F_SHIFT;
241         if (event->key.state & GDK_CONTROL_MASK) value |= TEO_KEY_F_CTRL;
242         if (event->key.state & GDK_MOD3_MASK)    value |= TEO_KEY_F_ALTGR;
243         if (event->key.state & GDK_MOD2_MASK)    value |= TEO_KEY_F_NUMLOCK;
244         if (event->key.state & GDK_LOCK_MASK)    value |= TEO_KEY_F_CAPSLOCK;
245         keyboard_Reset ((1<<TEO_KEY_F_MAX)-1, value);
246         need_modifiers_reset = FALSE;
247     }
248 
249     teo_key = x11_to_dos[event->key.hardware_keycode];
250     if (teo_key == 0)
251     {
252         if (event->key.keyval == GDK_KEY_ISO_Level3_Shift)
253             teo_key = TEO_KEY_ALTGR;
254 
255         /* Convert delete keypad key to point character
256          *  Over the time, Linux keypad delete key raw code has changed from
257          *  GDK_KEY_KP_Delete to GDK_KEY_KP_Decimal and case GDK_KEY_period,
258          *  so the ASCII corresponding value is now checked directly
259          *  with the 0x2e value.
260          */
261         if (event->key.keyval == 0x2e)
262             teo_key = TEO_KEY_DEL_PAD;
263     }
264 
265     switch (teo_key)
266     {
267         case TEO_KEY_ESC : teo.command=TEO_COMMAND_PANEL; break;
268         case TEO_KEY_F12 : teo.command=TEO_COMMAND_DEBUGGER; break;
269         default          : keyboard_Press (teo_key, FALSE); break;
270     }
271     return FALSE;
272     (void)widget;
273     (void)user_data;
274 }
275 
276 
277 /* key_release_event:
278  *  Gestion des touches relach�es.
279  */
280 static gboolean
key_release_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)281 key_release_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
282 {
283     int teo_key = x11_to_dos[event->key.hardware_keycode];
284 
285     if (teo_key == 0)
286         if (event->key.keyval == GDK_KEY_ISO_Level3_Shift)
287             teo_key = TEO_KEY_ALTGR;
288 
289     keyboard_Press (teo_key, TRUE);
290     return FALSE;
291     (void)widget;
292     (void)user_data;
293 }
294 
295 
296 /* button_press_event:
297  *  Gestion des boutons de souris enfonc�s.
298  */
299 static gboolean
button_press_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)300 button_press_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
301 {
302     switch (event->button.button)
303     {
304         case 1 : mouse_Click(1, FALSE); break;
305         case 3 : if (installed_pointer == TEO_STATUS_MOUSE)
306                      mouse_Click (2, FALSE);
307                  break;
308     }
309     return FALSE;
310     (void)widget;
311     (void)user_data;
312 }
313 
314 
315 /* button_release_event:
316  *  Gestion des boutons de souris relach�s.
317  */
318 static gboolean
button_release_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)319 button_release_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
320 {
321     switch (event->button.button)
322     {
323         case 1 : mouse_Click (1, TRUE); break;
324         case 3 : mouse_Click (2, TRUE); break;
325     }
326     return FALSE;
327     (void)widget;
328     (void)user_data;
329 }
330 
331 
332 /* motion_notify_event:
333  *  Gestion des mouvements de la souris.
334  */
335 static gboolean
motion_notify_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)336 motion_notify_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
337 {
338     if (((int)event->button.x > (TEO_BORDER_W*2))
339      && ((int)event->button.y > (TEO_BORDER_H*2)))
340         mouse_Motion ((int)event->button.x/2-TEO_BORDER_W,
341                       (int)event->button.y/2-TEO_BORDER_H);
342     gdk_event_request_motions ((GdkEventMotion *) event);
343     return FALSE;
344     (void)widget;
345     (void)user_data;
346 }
347 
348 
349 /* focus_in_event:
350  *  Gestion des activations de fen�tres.
351  */
352 static gboolean
focus_in_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)353 focus_in_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
354 {
355     if (event->focus_change.in == TRUE)
356     {
357         keyboard_Reset (0, 0);
358         need_modifiers_reset = TRUE;
359     }
360     return FALSE;
361     (void)widget;
362     (void)user_data;
363 }
364 
365 
366 /* visibility_notify_event:
367  *  Gestion du retra�age de l'�cran.
368  */
369 static gboolean
visibility_notify_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)370 visibility_notify_event (GtkWidget *widget, GdkEvent *event, gpointer user_data)
371 {
372     if (event->visibility.state == GDK_VISIBILITY_UNOBSCURED)
373         ugraphic_Retrace(0, 0, TEO_SCREEN_W*2, TEO_SCREEN_H*2);
374 
375     return FALSE;
376     (void)widget;
377     (void)user_data;
378 }
379 
380 
381 /* ------------------------------------------------------------------------- */
382 
383 
384 /* udisplay_Init:
385  *  Ouvre la connexion avec le serveur X et initialise le clavier.
386  */
udisplay_Init(void)387 void udisplay_Init(void)
388 {
389     int i;
390     int ret1, ret2, ret3;
391 
392     /* Connexion au serveur X */
393     display=gdk_x11_get_default_xdisplay();
394     screen=DefaultScreen(display);
395 
396     /* Calcul de la table de conversion des keycodes */
397     for (i=0; i<KB_SIZE; i++)
398         x11_to_dos[XKeysymToKeycode(display,keyconv[i].keysym)]=keyconv[i].keycode;
399 
400     /* Test de pr�sence de l'extension MIT-SHM */
401     mit_shm_enabled = XQueryExtension(display, "MIT-SHM", &ret1, &ret2, &ret3);
402 }
403 
404 
405 
406 /* udisplay_Window:
407  *   Cr�e la fen�tre principale.
408  */
udisplay_Window(void)409 void udisplay_Window(void)
410 {
411     GdkPixbuf *pixbuf;
412     GdkGeometry hints;
413     GtkCssProvider *provider;
414     GtkStyleContext *context;
415 
416     wMain = gtk_window_new (GTK_WINDOW_TOPLEVEL);
417 
418     gtk_window_set_resizable (GTK_WINDOW(wMain), FALSE);
419     gtk_window_set_title (GTK_WINDOW(wMain),
420                           is_fr?"Teo - l'émulateur TO8 (menu:ESC/débogueur:F12)"
421                                :"Teo - thomson TO8 emulator (menu:ESC/debugger:F12)");
422 
423     gtk_widget_add_events (wMain,
424                      GDK_FOCUS_CHANGE_MASK
425                    | GDK_VISIBILITY_NOTIFY_MASK
426                    | GDK_KEY_RELEASE_MASK
427                    | GDK_KEY_PRESS_MASK
428                    | GDK_STRUCTURE_MASK
429                    | GDK_BUTTON_RELEASE_MASK
430                    | GDK_BUTTON_PRESS_MASK
431                    | GDK_POINTER_MOTION_MASK);
432 
433     g_signal_connect (G_OBJECT (wMain), "delete-event",
434                       G_CALLBACK (delete_event), NULL);
435     g_signal_connect (G_OBJECT (wMain), "key-press-event",
436                       G_CALLBACK (key_press_event), NULL);
437     g_signal_connect (G_OBJECT (wMain), "key-release-event",
438                       G_CALLBACK (key_release_event), NULL);
439     g_signal_connect (G_OBJECT (wMain), "button-press-event",
440                       G_CALLBACK (button_press_event), NULL);
441     g_signal_connect (G_OBJECT (wMain), "button-release-event",
442                       G_CALLBACK (button_release_event), NULL);
443     g_signal_connect (G_OBJECT (wMain), "motion-notify-event",
444                       G_CALLBACK (motion_notify_event), NULL);
445     g_signal_connect (G_OBJECT (wMain), "focus-in-event",
446                       G_CALLBACK (focus_in_event), NULL);
447     g_signal_connect (G_OBJECT (wMain), "visibility-notify-event",
448                       G_CALLBACK (visibility_notify_event), NULL);
449 
450     /* Set window size */
451     hints.min_width = TEO_SCREEN_W*2;
452     hints.max_width = TEO_SCREEN_W*2;
453     hints.min_height = TEO_SCREEN_H*2;
454     hints.max_height = TEO_SCREEN_H*2;
455     gtk_window_set_geometry_hints (GTK_WINDOW(wMain), wMain, &hints,
456                                    GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
457 
458     /* Move the window */
459     gtk_window_move (GTK_WINDOW(wMain), 0, 0);
460 
461     /* Set program icon */
462     pixbuf=gdk_pixbuf_new_from_resource("/net/sourceforge/teoemulator/teo.png", NULL);
463     gtk_window_set_icon (GTK_WINDOW(wMain),pixbuf);
464     gtk_window_set_default_icon(pixbuf);
465 
466     /* Set black background */
467     provider = gtk_css_provider_new ();
468     gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider),
469                                      "* { background-color: #000000; }",
470                                      -1, NULL);
471     context = gtk_widget_get_style_context (wMain);
472     gtk_style_context_add_provider (context,
473                                     GTK_STYLE_PROVIDER (provider),
474                                     GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
475     g_object_unref (provider);
476 
477     gtk_widget_set_double_buffered (wMain, FALSE);  /* only one buffer for drawing */
478     gtk_widget_set_app_paintable (wMain, TRUE);
479     gtk_widget_set_can_focus (wMain, TRUE);
480 
481     gtk_widget_show_all (wMain);
482 
483     gwindow_win = gtk_widget_get_window (wMain);
484 
485 #ifndef SCAN_DEPEND
486 #if GTK_CHECK_VERSION(3,12,0)
487     gdk_window_set_event_compression (gwindow_win, FALSE);
488 #endif
489 #endif
490     window_win = GDK_WINDOW_XID (gwindow_win);
491     screen_win = window_win;
492 
493     teo_SetPointer=SetPointer;
494 
495     printf("ok\n");
496 }
497 
498