4 * Define the main (emu) GTK window, along with its menubars,
7 * Copyright (c) 2005 Nathan Keynes.
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.
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.
21 #include <sys/types.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <X11/Xutil.h>
34 #include "dreamcast.h"
36 #include "gdrom/gdrom.h"
37 #include "gtkui/gtkui.h"
40 struct main_window_info {
46 GtkActionGroup *actions;
49 int32_t mouse_x, mouse_y;
53 /******************** Video window **************************/
55 #if !(GTK_CHECK_VERSION(2,8,0))
56 void gdk_display_warp_pointer (GdkDisplay *display,
63 * Adjust the mouse pointer so that it appears in the center of the video
64 * window. Mainly used for when we have the mouse grab
66 void video_window_center_pointer( main_window_t win )
68 GdkDisplay *display = gtk_widget_get_display(win->video);
69 GdkScreen *screen = gtk_widget_get_screen(win->video);
73 gdk_window_get_origin(win->video->window, &x, &y);
74 gdk_drawable_get_size(GDK_DRAWABLE(win->video->window), &width, &height);
78 gdk_display_warp_pointer( display, screen, x, y );
79 win->mouse_x = width/2;
80 win->mouse_y = height/2;
84 * Grab the keyboard and mouse for the display. The mouse cursor is hidden and
85 * moved to the centre of the window.
87 * @param win The window receiving the grab
88 * @return TRUE if the grab was successful, FALSE on failure.
90 gboolean video_window_grab_display( main_window_t win )
92 GdkWindow *gdkwin = win->video->window;
93 GdkColor color = { 0,0,0,0 };
94 char bytes[32]; /* 16 * 16 / 8 */
96 GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bytes, 16, 16);
97 GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &color, &color, 16, 16);
98 gdk_pixmap_unref(pixmap);
101 gdk_pointer_grab( gdkwin, FALSE,
102 GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK,
103 gdkwin, cursor, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
104 gdk_cursor_unref(cursor);
106 success = gdk_keyboard_grab( gdkwin, FALSE, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
108 gdk_pointer_ungrab(GDK_CURRENT_TIME);
111 win->is_grabbed = success;
112 main_window_set_running(win, dreamcast_is_running());
117 * Release the display grab.
119 void video_window_ungrab_display( main_window_t win )
121 gdk_pointer_ungrab(GDK_CURRENT_TIME);
122 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
123 win->is_grabbed = FALSE;
124 main_window_set_running(win, dreamcast_is_running());
127 static gboolean on_video_window_mouse_motion( GtkWidget *widget, GdkEventMotion *event,
130 main_window_t win = (main_window_t)user_data;
131 int32_t x = (int32_t)event->x;
132 int32_t y = (int32_t)event->y;
133 if( win->is_grabbed &&
134 (x != win->mouse_x || y != win->mouse_y) ) {
135 uint32_t buttons = (event->state >> 8)&0x1F;
136 input_event_mouse( buttons, x - win->mouse_x, y - win->mouse_y, FALSE );
137 video_window_center_pointer(win);
142 static gboolean on_video_window_mouse_pressed( GtkWidget *widget, GdkEventButton *event,
145 main_window_t win = (main_window_t)user_data;
146 // Get the buttons from the event state, and add the pressed button
147 uint32_t buttons = ((event->state >> 8) & 0x1F) | (1<<(event->button-1));
148 if( win->is_grabbed ) {
149 input_event_mouse( buttons, 0, 0, FALSE );
151 input_event_mouse( buttons, (int)event->x, (int)event->y, TRUE );
156 static gboolean on_video_window_mouse_released( GtkWidget *widget, GdkEventButton *event,
159 main_window_t win = (main_window_t)user_data;
160 // Get the buttons from the event state, and remove the released button
161 uint32_t buttons = ((event->state >> 8) & 0x1F) & (~(1<<(event->button-1)));
162 if( win->is_grabbed ) {
163 input_event_mouse( buttons, 0, 0, FALSE );
164 } else if( win->use_grab) {
165 video_window_grab_display(win);
167 input_event_mouse( buttons, (int)event->x, (int)event->y, TRUE );
172 static gboolean on_video_window_key_pressed( GtkWidget *widget, GdkEventKey *event,
175 main_window_t win = (main_window_t)user_data;
176 if( win->is_grabbed ) {
178 /* On OSX, use the command key rather than ctrl-alt. Mainly because GTK/OSX
179 * doesn't seem to be able to get ctrl-alt reliably
181 if( event->keyval == GDK_Meta_L || event->keyval == GDK_Meta_R ) {
182 video_window_ungrab_display(win);
186 /* Check for ungrab key combo (ctrl-alt). Unfortunately GDK sends it as
187 * a singly-modified keypress rather than a double-modified 'null' press,
188 * so we have to do a little more work.
189 * Only check Ctrl/Shift/Alt for state - don't want to check numlock/capslock/
192 int mod = gdk_keycode_to_modifier(gtk_widget_get_display(widget), event->hardware_keycode);
193 int state = event->state & gtk_accelerator_get_default_mod_mask();
194 if( (state == GDK_CONTROL_MASK && mod == GDK_MOD1_MASK) ||
195 (state == GDK_MOD1_MASK && mod == GDK_CONTROL_MASK) ) {
196 video_window_ungrab_display(win);
197 // Consume the keypress, DC doesn't get it.
202 input_event_keydown( NULL, gtk_get_unmodified_keyval(event), 1 );
206 static gboolean on_video_window_key_released( GtkWidget *widget, GdkEventKey *event,
209 input_event_keyup( NULL, gtk_get_unmodified_keyval(event), 0 );
213 static gboolean on_video_window_focus_changed( GtkWidget *widget, GdkEventFocus *event,
216 display_set_focused(event->in);
220 /*************************** Main window (frame) ******************************/
222 static gboolean on_main_window_deleted( GtkWidget *widget, GdkEvent event, gpointer user_data )
224 dreamcast_shutdown();
228 static void on_main_window_state_changed( GtkWidget *widget, GdkEventWindowState *state,
231 main_window_t win = (main_window_t)userdata;
232 if( state->changed_mask & GDK_WINDOW_STATE_FULLSCREEN ) {
233 gboolean fs = (state->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
234 GtkWidget *frame = gtk_widget_get_parent(win->video);
235 if( frame->style == NULL ) {
236 gtk_widget_set_style( frame, gtk_style_new() );
239 gtk_widget_hide( win->menubar );
240 gtk_widget_hide( win->toolbar );
241 gtk_widget_hide( win->statusbar );
243 frame->style->xthickness = 0;
244 frame->style->ythickness = 0;
246 frame->style->xthickness = 2;
247 frame->style->ythickness = 2;
248 gtk_widget_show( win->menubar );
249 gtk_widget_show( win->toolbar );
250 gtk_widget_show( win->statusbar );
252 gtk_widget_queue_draw( win->window );
256 main_window_t main_window_new( const gchar *title, GtkWidget *menubar, GtkWidget *toolbar,
257 GtkAccelGroup *accel_group )
261 main_window_t win = g_malloc0( sizeof(struct main_window_info) );
263 win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
264 win->menubar = menubar;
265 win->toolbar = toolbar;
266 win->use_grab = FALSE;
267 win->is_grabbed = FALSE;
268 gtk_window_set_title( GTK_WINDOW(win->window), title );
269 gtk_window_add_accel_group (GTK_WINDOW (win->window), accel_group);
271 gtk_toolbar_set_style( GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS );
273 win->video = video_gtk_create_drawable();
274 gtk_widget_set_size_request( win->video, 640, 480 );
275 gtk_widget_set_double_buffered( win->video, FALSE );
276 frame = gtk_frame_new(NULL);
277 gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_IN );
278 gtk_container_add( GTK_CONTAINER(frame), win->video );
280 win->statusbar = gtk_statusbar_new();
282 vbox = gtk_vbox_new(FALSE, 0);
283 gtk_container_add( GTK_CONTAINER(win->window), vbox );
284 gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, FALSE, 0 );
285 gtk_box_pack_start( GTK_BOX(vbox), toolbar, FALSE, FALSE, 0 );
286 gtk_box_pack_start( GTK_BOX(vbox), frame, TRUE, TRUE, 0 );
287 gtk_box_pack_start( GTK_BOX(vbox), win->statusbar, FALSE, FALSE, 0 );
288 gtk_widget_show_all( win->window );
289 gtk_widget_grab_focus( win->video );
291 gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, "Stopped" );
293 g_signal_connect( win->window, "delete_event",
294 G_CALLBACK(on_main_window_deleted), win );
295 g_signal_connect( win->window, "window-state-event",
296 G_CALLBACK(on_main_window_state_changed), win );
298 g_signal_connect( win->video, "key-press-event",
299 G_CALLBACK(on_video_window_key_pressed), win );
300 g_signal_connect( win->video, "key-release-event",
301 G_CALLBACK(on_video_window_key_released), win );
302 g_signal_connect( win->video, "motion-notify-event",
303 G_CALLBACK(on_video_window_mouse_motion), win );
304 g_signal_connect( win->video, "button-press-event",
305 G_CALLBACK(on_video_window_mouse_pressed), win );
306 g_signal_connect( win->video, "button-release-event",
307 G_CALLBACK(on_video_window_mouse_released), win );
308 g_signal_connect( win->video, "focus-in-event",
309 G_CALLBACK(on_video_window_focus_changed), win);
310 g_signal_connect( win->video, "focus-out-event",
311 G_CALLBACK(on_video_window_focus_changed), win);
313 gtk_widget_add_events( win->video,
314 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
315 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
316 GDK_POINTER_MOTION_MASK | GDK_FOCUS_CHANGE_MASK );
321 void main_window_set_status_text( main_window_t win, char *text )
323 gtk_statusbar_pop( GTK_STATUSBAR(win->statusbar), 1 );
324 if( win->is_grabbed ) {
327 snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <command> to release grab)") );
329 snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <ctrl><alt> to release grab)") );
331 gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, buf );
333 gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, text );
337 void main_window_set_running( main_window_t win, gboolean running )
339 char *text = running ? _("Running") : _("Stopped");
340 gtk_gui_enable_action( "Pause", running );
341 gtk_gui_enable_action( "Run", !running && dreamcast_can_run() );
342 main_window_set_status_text( win, text );
345 void main_window_set_framerate( main_window_t win, float rate )
351 void main_window_set_speed( main_window_t win, double speed )
355 snprintf( buf, 32, "Running (%2.4f%%)", speed );
356 main_window_set_status_text( win, buf );
359 GtkWidget *main_window_get_renderarea( main_window_t win )
364 GtkWindow *main_window_get_frame( main_window_t win )
366 return GTK_WINDOW(win->window);
369 void main_window_set_fullscreen( main_window_t win, gboolean fullscreen )
372 gtk_window_fullscreen( GTK_WINDOW(win->window) );
374 gtk_window_unfullscreen( GTK_WINDOW(win->window) );
378 void main_window_set_use_grab( main_window_t win, gboolean use_grab )
380 if( use_grab != win->use_grab ) {
382 GdkCursor *cursor = gdk_cursor_new( GDK_HAND2 );
383 gdk_window_set_cursor( win->video->window, cursor );
384 gdk_cursor_unref( cursor );
386 gdk_window_set_cursor( win->video->window, NULL );
387 if( gdk_pointer_is_grabbed() ) {
388 video_window_ungrab_display(win);
391 win->use_grab = use_grab;
395 void main_window_update_title( main_window_t win )
397 const char *disc = gdrom_get_current_disc_title();
400 gtk_window_set_title( GTK_WINDOW(win->window), lxdream_package_name );
403 snprintf( buf, sizeof(buf), "%s - %s", lxdream_package_name, disc );
404 gtk_window_set_title( GTK_WINDOW(win->window), buf );
.