filename | src/gtkui/gtk_win.c |
changeset | 1098:4f2750753a6c |
prev | 1066:ddffe9d2b332 |
author | nkeynes |
date | Mon Jan 30 20:11:08 2012 +1000 (12 years ago) |
permissions | -rw-r--r-- |
last change | Replace wordexp() with a hand-coded env-var substitution. More portable, and avoids bugs with some wordexp() implementations |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * Define the main (emu) GTK window, along with its menubars,
5 * toolbars, etc.
6 *
7 * Copyright (c) 2005 Nathan Keynes.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
20 #include <assert.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdlib.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include <gtk/gtk.h>
31 #include <gdk/gdk.h>
32 #include <gdk/gdkkeysyms.h>
34 #include "lxdream.h"
35 #include "dreamcast.h"
36 #include "display.h"
37 #include "gdrom/gdrom.h"
38 #include "gtkui/gtkui.h"
39 #include "drivers/video_gl.h"
42 struct main_window_info {
43 GtkWidget *window;
44 GtkWidget *video;
45 GtkWidget *menubar;
46 GtkWidget *toolbar;
47 GtkWidget *statusbar;
48 GtkActionGroup *actions;
49 gboolean use_grab;
50 gboolean is_grabbed;
51 int32_t mouse_x, mouse_y;
52 };
55 /******************** Video window **************************/
57 #if !(GTK_CHECK_VERSION(2,8,0))
58 void gdk_display_warp_pointer (GdkDisplay *display,
59 GdkScreen *screen,
60 gint x,
61 gint y);
62 #endif
64 /**
65 * Adjust the mouse pointer so that it appears in the center of the video
66 * window. Mainly used for when we have the mouse grab
67 */
68 void video_window_center_pointer( main_window_t win )
69 {
70 GdkDisplay *display = gtk_widget_get_display(win->video);
71 GdkScreen *screen = gtk_widget_get_screen(win->video);
72 int x,y;
73 int width, height;
75 gdk_window_get_origin(win->video->window, &x, &y);
76 gdk_drawable_get_size(GDK_DRAWABLE(win->video->window), &width, &height);
77 x += width / 2;
78 y += height / 2;
80 gdk_display_warp_pointer( display, screen, x, y );
81 win->mouse_x = width/2;
82 win->mouse_y = height/2;
83 }
85 /**
86 * Grab the keyboard and mouse for the display. The mouse cursor is hidden and
87 * moved to the centre of the window.
88 *
89 * @param win The window receiving the grab
90 * @return TRUE if the grab was successful, FALSE on failure.
91 */
92 gboolean video_window_grab_display( main_window_t win )
93 {
94 GdkWindow *gdkwin = win->video->window;
95 GdkColor color = { 0,0,0,0 };
96 char bytes[32]; /* 16 * 16 / 8 */
97 memset(bytes, 0, 32);
98 GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bytes, 16, 16);
99 GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &color, &color, 16, 16);
100 gdk_pixmap_unref(pixmap);
102 gboolean success =
103 gdk_pointer_grab( gdkwin, FALSE,
104 GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK,
105 gdkwin, cursor, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
106 gdk_cursor_unref(cursor);
107 if( success ) {
108 success = gdk_keyboard_grab( gdkwin, FALSE, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
109 if( !success ) {
110 gdk_pointer_ungrab(GDK_CURRENT_TIME);
111 }
112 }
113 win->is_grabbed = success;
114 main_window_set_running(win, dreamcast_is_running());
115 return success;
116 }
118 /**
119 * Release the display grab.
120 */
121 void video_window_ungrab_display( main_window_t win )
122 {
123 gdk_pointer_ungrab(GDK_CURRENT_TIME);
124 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
125 win->is_grabbed = FALSE;
126 main_window_set_running(win, dreamcast_is_running());
127 }
129 static gboolean on_video_window_mouse_motion( GtkWidget *widget, GdkEventMotion *event,
130 gpointer user_data )
131 {
132 main_window_t win = (main_window_t)user_data;
133 int x = (int)event->x;
134 int y = (int)event->y;
135 if( win->is_grabbed &&
136 (x != win->mouse_x || y != win->mouse_y) ) {
137 input_event_mousemove( x - win->mouse_x, y - win->mouse_y, FALSE );
138 video_window_center_pointer(win);
139 } else {
140 int width, height;
141 gl_window_to_system_coords( &x, &y );
142 input_event_mousemove( x, y, TRUE );
143 }
144 return TRUE;
145 }
147 static gboolean on_video_window_mouse_exited( GtkWidget *widget, GdkEventCrossing *event,
148 gpointer user_data )
149 {
150 main_window_t win = (main_window_t)user_data;
151 if( !win->is_grabbed ) {
152 input_event_mousemove( -1, -1, TRUE );
153 }
154 return TRUE;
155 }
157 static gboolean on_video_window_mouse_pressed( GtkWidget *widget, GdkEventButton *event,
158 gpointer user_data )
159 {
160 main_window_t win = (main_window_t)user_data;
161 if( win->is_grabbed ) {
162 input_event_mousedown( event->button-1, 0, 0, FALSE );
163 } else {
164 int x = (int)event->x;
165 int y = (int)event->y;
166 gl_window_to_system_coords( &x, &y );
167 input_event_mousedown( event->button-1, x, y, TRUE );
168 }
169 return TRUE;
170 }
172 static gboolean on_video_window_mouse_released( GtkWidget *widget, GdkEventButton *event,
173 gpointer user_data )
174 {
175 main_window_t win = (main_window_t)user_data;
176 if( win->is_grabbed ) {
177 input_event_mouseup( event->button-1, 0, 0, FALSE );
178 } else if( win->use_grab) {
179 video_window_grab_display(win);
180 } else {
181 int x = (int)event->x;
182 int y = (int)event->y;
183 gl_window_to_system_coords( &x, &y );
184 input_event_mouseup( event->button-1, x, y, TRUE );
185 }
186 return TRUE;
187 }
189 static gboolean on_video_window_key_pressed( GtkWidget *widget, GdkEventKey *event,
190 gpointer user_data )
191 {
192 main_window_t win = (main_window_t)user_data;
193 if( win->is_grabbed ) {
194 #ifdef HAVE_GTK_OSX
195 /* On OSX, use the command key rather than ctrl-alt. Mainly because GTK/OSX
196 * doesn't seem to be able to get ctrl-alt reliably
197 **/
198 if( event->keyval == GDK_Meta_L || event->keyval == GDK_Meta_R ) {
199 video_window_ungrab_display(win);
200 return TRUE;
201 }
202 #else
203 /* Check for ungrab key combo (ctrl-alt). Unfortunately GDK sends it as
204 * a singly-modified keypress rather than a double-modified 'null' press,
205 * so we have to do a little more work.
206 * Only check Ctrl/Shift/Alt for state - don't want to check numlock/capslock/
207 * mouse buttons/etc
208 */
209 int mod = gdk_keycode_to_modifier(gtk_widget_get_display(widget), event->hardware_keycode);
210 int state = event->state & gtk_accelerator_get_default_mod_mask();
211 if( (state == GDK_CONTROL_MASK && mod == GDK_MOD1_MASK) ||
212 (state == GDK_MOD1_MASK && mod == GDK_CONTROL_MASK) ) {
213 video_window_ungrab_display(win);
214 // Consume the keypress, DC doesn't get it.
215 return TRUE;
216 }
217 #endif
218 }
219 input_event_keydown( NULL, gtk_get_unmodified_keyval(event), MAX_PRESSURE );
220 return TRUE;
221 }
223 static gboolean on_video_window_key_released( GtkWidget *widget, GdkEventKey *event,
224 gpointer user_data )
225 {
226 input_event_keyup( NULL, gtk_get_unmodified_keyval(event) );
227 return TRUE;
228 }
230 static gboolean on_video_window_focus_changed( GtkWidget *widget, GdkEventFocus *event,
231 gpointer user_data )
232 {
233 display_set_focused(event->in);
234 return TRUE;
235 }
237 /*************************** Main window (frame) ******************************/
239 static gboolean on_main_window_deleted( GtkWidget *widget, GdkEvent event, gpointer user_data )
240 {
241 dreamcast_shutdown();
242 exit(0);
243 }
245 static void on_main_window_state_changed( GtkWidget *widget, GdkEventWindowState *state,
246 gpointer userdata )
247 {
248 main_window_t win = (main_window_t)userdata;
249 if( state->changed_mask & GDK_WINDOW_STATE_FULLSCREEN ) {
250 gboolean fs = (state->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
251 main_window_show_gui(win, fs);
252 }
253 }
255 void main_window_show_gui(main_window_t win, gboolean fullscreen)
256 {
257 GtkWidget *frame = gtk_widget_get_parent(win->video);
258 if( frame->style == NULL ) {
259 gtk_widget_set_style( frame, gtk_style_new() );
260 }
261 if( fullscreen ) {
262 gtk_widget_hide( win->menubar );
263 gtk_widget_hide( win->toolbar );
264 gtk_widget_hide( win->statusbar );
266 frame->style->xthickness = 0;
267 frame->style->ythickness = 0;
268 } else {
269 frame->style->xthickness = 2;
270 frame->style->ythickness = 2;
271 gtk_widget_show( win->menubar );
272 gtk_widget_show( win->toolbar );
273 gtk_widget_show( win->statusbar );
274 }
275 gtk_widget_queue_draw( win->window );
276 }
278 main_window_t main_window_new( const gchar *title, GtkWidget *menubar, GtkWidget *toolbar,
279 GtkAccelGroup *accel_group )
280 {
281 GtkWidget *vbox;
282 GtkWidget *frame;
283 main_window_t win = g_malloc0( sizeof(struct main_window_info) );
285 win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
286 win->menubar = menubar;
287 win->toolbar = toolbar;
288 win->use_grab = FALSE;
289 win->is_grabbed = FALSE;
290 gtk_window_set_title( GTK_WINDOW(win->window), title );
291 gtk_window_add_accel_group (GTK_WINDOW (win->window), accel_group);
292 gtk_window_set_icon_from_file( GTK_WINDOW(win->window),
293 PACKAGE_DATA_DIR "/pixmaps/lxdream.png", NULL );
295 gtk_toolbar_set_style( GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS );
297 win->video = video_gtk_create_drawable();
298 gtk_widget_set_size_request( win->video, 640, 480 );
299 gtk_widget_set_double_buffered( win->video, FALSE );
300 frame = gtk_frame_new(NULL);
301 gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_IN );
302 gtk_container_add( GTK_CONTAINER(frame), win->video );
304 win->statusbar = gtk_statusbar_new();
306 vbox = gtk_vbox_new(FALSE, 0);
307 gtk_container_add( GTK_CONTAINER(win->window), vbox );
308 gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, FALSE, 0 );
309 gtk_box_pack_start( GTK_BOX(vbox), toolbar, FALSE, FALSE, 0 );
310 gtk_box_pack_start( GTK_BOX(vbox), frame, TRUE, TRUE, 0 );
311 gtk_box_pack_start( GTK_BOX(vbox), win->statusbar, FALSE, FALSE, 0 );
312 gtk_widget_show_all( win->window );
313 gtk_widget_grab_focus( win->video );
315 gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, "Stopped" );
317 g_signal_connect( win->window, "delete_event",
318 G_CALLBACK(on_main_window_deleted), win );
319 g_signal_connect( win->window, "window-state-event",
320 G_CALLBACK(on_main_window_state_changed), win );
322 g_signal_connect( win->video, "key-press-event",
323 G_CALLBACK(on_video_window_key_pressed), win );
324 g_signal_connect( win->video, "key-release-event",
325 G_CALLBACK(on_video_window_key_released), win );
326 g_signal_connect( win->video, "motion-notify-event",
327 G_CALLBACK(on_video_window_mouse_motion), win );
328 g_signal_connect( win->video, "leave-notify-event",
329 G_CALLBACK(on_video_window_mouse_exited), win );
330 g_signal_connect( win->video, "button-press-event",
331 G_CALLBACK(on_video_window_mouse_pressed), win );
332 g_signal_connect( win->video, "button-release-event",
333 G_CALLBACK(on_video_window_mouse_released), win );
334 g_signal_connect( win->video, "focus-in-event",
335 G_CALLBACK(on_video_window_focus_changed), win);
336 g_signal_connect( win->video, "focus-out-event",
337 G_CALLBACK(on_video_window_focus_changed), win);
339 gtk_widget_add_events( win->video,
340 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
341 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
342 GDK_POINTER_MOTION_MASK | GDK_FOCUS_CHANGE_MASK |
343 GDK_LEAVE_NOTIFY_MASK );
345 return win;
346 }
348 void main_window_set_status_text( main_window_t win, const char *text )
349 {
350 gtk_statusbar_pop( GTK_STATUSBAR(win->statusbar), 1 );
351 if( win->is_grabbed ) {
352 char buf[128];
353 #ifdef HAVE_GTK_OSX
354 snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <command> to release grab)") );
355 #else
356 snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <ctrl><alt> to release grab)") );
357 #endif
358 gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, buf );
359 } else {
360 gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, text );
361 }
362 }
364 void main_window_set_running( main_window_t win, gboolean running )
365 {
366 const char *text = running ? _("Running") : _("Stopped");
367 gtk_gui_enable_action( "Pause", running );
368 gtk_gui_enable_action( "Run", !running );
369 main_window_set_status_text( win, text );
370 }
372 void main_window_set_framerate( main_window_t win, float rate )
373 {
376 }
378 void main_window_set_speed( main_window_t win, double speed )
379 {
380 char buf[32];
382 snprintf( buf, 32, "Running (%2.4f%%)", speed );
383 main_window_set_status_text( win, buf );
384 }
386 GtkWidget *main_window_get_renderarea( main_window_t win )
387 {
388 return win->video;
389 }
391 GtkWindow *main_window_get_frame( main_window_t win )
392 {
393 return GTK_WINDOW(win->window);
394 }
396 void main_window_set_fullscreen( main_window_t win, gboolean fullscreen )
397 {
398 if( fullscreen ) {
399 gtk_window_fullscreen( GTK_WINDOW(win->window) );
400 } else {
401 gtk_window_unfullscreen( GTK_WINDOW(win->window) );
402 }
403 }
405 void main_window_set_use_grab( main_window_t win, gboolean use_grab )
406 {
407 if( use_grab != win->use_grab ) {
408 if( use_grab ) {
409 GdkCursor *cursor = gdk_cursor_new( GDK_HAND2 );
410 gdk_window_set_cursor( win->video->window, cursor );
411 gdk_cursor_unref( cursor );
412 } else {
413 gdk_window_set_cursor( win->video->window, NULL );
414 if( gdk_pointer_is_grabbed() ) {
415 video_window_ungrab_display(win);
416 }
417 }
418 win->use_grab = use_grab;
419 }
420 }
422 void main_window_update_title( main_window_t win )
423 {
424 const char *disc = gdrom_get_current_disc_title();
425 char buf[256];
427 if( disc == NULL ) {
428 snprintf( buf, sizeof(buf), "%s - <%s>", lxdream_package_name, _("no disc") );
429 } else {
430 snprintf( buf, sizeof(buf), "%s - %s", lxdream_package_name, disc );
431 }
432 gtk_window_set_title( GTK_WINDOW(win->window), buf );
433 }
.