Search
lxdream.org :: lxdream/src/gtkui/main_win.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gtkui/main_win.c
changeset 614:a2d239d4438a
prev612:410b48e63d53
next618:3ade50e8603c
author nkeynes
date Mon Jan 28 02:38:09 2008 +0000 (14 years ago)
permissions -rw-r--r--
last change Bug #49: Joystick support work in progress
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 <gtk/gtk.h>
    29 #include <gdk/gdk.h>
    30 #include <gdk/gdkx.h>
    31 #include <gdk/gdkkeysyms.h>
    32 #include <X11/Xutil.h>
    34 #include "dream.h"
    35 #include "gtkui/gtkui.h"
    36 #include "drivers/video_glx.h"
    39 struct main_window_info {
    40     GtkWidget *window;
    41     GtkWidget *video;
    42     GtkWidget *menubar;
    43     GtkWidget *toolbar;
    44     GtkWidget *statusbar;
    45     GtkActionGroup *actions;
    46     gboolean use_grab;
    47     gboolean is_grabbed;
    48     int32_t mouse_x, mouse_y;
    49 };
    52 /******************** Video window **************************/
    54 /**
    55  * Adjust the mouse pointer so that it appears in the center of the video
    56  * window. Mainly used for when we have the mouse grab
    57  */
    58 void video_window_center_pointer( main_window_t win )
    59 {
    60     GdkDisplay *display = gtk_widget_get_display(win->video);
    61     GdkScreen *screen = gtk_widget_get_screen(win->video);
    62     int x,y;
    63     int width, height;
    65     gdk_window_get_origin(win->video->window, &x, &y);
    66     gdk_drawable_get_size(GDK_DRAWABLE(win->video->window), &width, &height);
    67     x += width / 2;
    68     y += height / 2;
    70     gdk_display_warp_pointer( display, screen, x, y );
    71     win->mouse_x = width/2;
    72     win->mouse_y = height/2;
    73 }
    75 /**
    76  * Grab the keyboard and mouse for the display. The mouse cursor is hidden and
    77  * moved to the centre of the window.
    78  *
    79  * @param win The window receiving the grab
    80  * @return TRUE if the grab was successful, FALSE on failure.
    81  */
    82 gboolean video_window_grab_display( main_window_t win )
    83 {
    84     GdkWindow *gdkwin = win->video->window;
    85     GdkColor color = { 0,0,0,0 };
    86     char bytes[32]; /* 16 * 16 / 8 */
    87     memset(bytes, 0, 32);
    88     GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bytes, 16, 16);
    89     GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &color, &color, 16, 16);
    90     gdk_pixmap_unref(pixmap);
    92     gboolean success =
    93 	gdk_pointer_grab( gdkwin, FALSE, 
    94 			  GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, 
    95 			  gdkwin, cursor, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
    96     gdk_cursor_unref(cursor);
    97     if( success ) {
    98 	success = gdk_keyboard_grab( gdkwin, FALSE, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
    99 	if( !success ) {
   100 	    gdk_pointer_ungrab(GDK_CURRENT_TIME);
   101 	}
   102     }
   103     win->is_grabbed = success;
   104     main_window_set_running(win, dreamcast_is_running());
   105     return success;
   106 }
   108 /**
   109  * Release the display grab.
   110  */
   111 void video_window_ungrab_display( main_window_t win )
   112 {
   113     gdk_pointer_ungrab(GDK_CURRENT_TIME);
   114     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
   115     win->is_grabbed = FALSE;
   116     main_window_set_running(win, dreamcast_is_running());
   117 }
   119 static gboolean on_video_window_mouse_motion( GtkWidget *widget, GdkEventMotion *event,
   120 					      gpointer user_data )
   121 {
   122     main_window_t win = (main_window_t)user_data;
   123     int32_t x = (int32_t)event->x;
   124     int32_t y = (int32_t)event->y;
   125     if( win->is_grabbed && 
   126 	(x != win->mouse_x || y != win->mouse_y) ) {
   127 	uint32_t buttons = (event->state >> 8)&0x1F;
   128 	input_event_mouse( buttons, x - win->mouse_x, y - win->mouse_y );
   129 	video_window_center_pointer(win);
   130     }
   131     return TRUE;
   132 }
   134 static gboolean on_video_window_mouse_pressed( GtkWidget *widget, GdkEventButton *event,
   135 					       gpointer user_data )
   136 {
   137     main_window_t win = (main_window_t)user_data;
   138     if( win->is_grabbed ) {
   139 	// Get the buttons from the event state, and remove the released button
   140 	uint32_t buttons = ((event->state >> 8) & 0x1F) | (1<<(event->button-1));
   141 	input_event_mouse( buttons, 0, 0 );
   142     }
   143     return TRUE;
   144 }
   146 static gboolean on_video_window_mouse_released( GtkWidget *widget, GdkEventButton *event,
   147 					      gpointer user_data )
   148 {
   149     main_window_t win = (main_window_t)user_data;
   150     if( win->is_grabbed ) {
   151 	// Get the buttons from the event state, and remove the released button
   152 	uint32_t buttons = ((event->state >> 8) & 0x1F) & (~(1<<(event->button-1)));
   153 	input_event_mouse( buttons, 0, 0 );
   154     } else if( win->use_grab) {
   155 	video_window_grab_display(win);
   156     }
   157     return TRUE;
   158 }
   160 static gboolean on_video_window_key_pressed( GtkWidget *widget, GdkEventKey *event,
   161 					    gpointer user_data )
   162 {
   163     main_window_t win = (main_window_t)user_data;
   164     if( win->is_grabbed ) {
   165 	/* Check for ungrab key combo (ctrl-alt). Unfortunately GDK sends it as
   166 	 * a singly-modified keypress rather than a double-modified 'null' press, 
   167 	 * so we have to do a little more work.
   168 	 * Only check Ctrl/Shift/Alt for state - don't want to check numlock/capslock/
   169 	 * mouse buttons/etc
   170 	 */
   171 	int state = event->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK);
   172 	if( (state == GDK_CONTROL_MASK &&
   173 	     (event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R)) ||
   174 	    (state == GDK_MOD1_MASK &&
   175 	     (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R)) ) {
   176 	    video_window_ungrab_display(win);
   177 	    // Consume the keypress, DC doesn't get it.
   178 	    return TRUE;
   179 	}
   180     }
   181     input_event_keydown( NULL, gtk_get_unmodified_keyval(event), 1 );
   182     return TRUE;
   183 }
   185 static gboolean on_video_window_key_released( GtkWidget *widget, GdkEventKey *event,
   186 					     gpointer user_data )
   187 {
   188     main_window_t win = (main_window_t)user_data;
   189     input_event_keyup( NULL, gtk_get_unmodified_keyval(event), 0 );
   190     return TRUE;
   191 }
   193 static gboolean on_video_window_grab_broken( GtkWidget *widget, GdkEventGrabBroken *event,
   194 					gpointer user_data )
   195 {
   196     main_window_t win = (main_window_t)user_data;
   197     fprintf( stderr, "Grab broken\n" );
   198 }
   200 static gboolean on_video_window_focus_changed( GtkWidget *widget, GdkEventFocus *event,
   201 					       gpointer user_data )
   202 {
   203     display_set_focused(event->in);
   204 }
   206 /*************************** Main window (frame) ******************************/
   208 static gboolean on_main_window_deleted( GtkWidget *widget, GdkEvent event, gpointer user_data )
   209 {
   210     exit(0);
   211 }
   213 static void on_main_window_state_changed( GtkWidget *widget, GdkEventWindowState *state, 
   214 					  gpointer userdata )
   215 {
   216     main_window_t win = (main_window_t)userdata;
   217     if( state->changed_mask & GDK_WINDOW_STATE_FULLSCREEN ) {
   218 	gboolean fs = (state->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
   219 	GtkWidget *frame = gtk_widget_get_parent(win->video);
   220 	if( frame->style == NULL ) {
   221 	    gtk_widget_set_style( frame, gtk_style_new() );
   222 	}
   223 	if( fs ) {
   224 	    gtk_widget_hide( win->menubar );
   225 	    gtk_widget_hide( win->toolbar );
   226 	    gtk_widget_hide( win->statusbar );
   228 	    frame->style->xthickness = 0;
   229 	    frame->style->ythickness = 0;
   230 	} else {
   231 	    frame->style->xthickness = 2;
   232 	    frame->style->ythickness = 2;
   233 	    gtk_widget_show( win->menubar );
   234 	    gtk_widget_show( win->toolbar );
   235 	    gtk_widget_show( win->statusbar );
   236 	}
   237 	gtk_widget_queue_draw( win->window );
   238     }
   239 }
   241 main_window_t main_window_new( const gchar *title, GtkWidget *menubar, GtkWidget *toolbar,
   242 			       GtkAccelGroup *accel_group )
   243 {
   244     GtkWidget *vbox;
   245     GtkWidget *frame;
   246     main_window_t win = g_malloc0( sizeof(struct main_window_info) );
   248     win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   249     win->menubar = menubar;
   250     win->toolbar = toolbar;
   251     win->use_grab = FALSE;
   252     win->is_grabbed = FALSE;
   253     gtk_window_set_title( GTK_WINDOW(win->window), title );
   254     gtk_window_add_accel_group (GTK_WINDOW (win->window), accel_group);
   256     gtk_toolbar_set_style( GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS );
   258     Display *display = gdk_x11_display_get_xdisplay( gtk_widget_get_display(win->window));
   259     Screen *screen = gdk_x11_screen_get_xscreen( gtk_widget_get_screen(win->window));
   260     int screen_no = XScreenNumberOfScreen(screen);
   261     if( !video_glx_init(display, screen_no) ) {
   262         ERROR( "Unable to initialize GLX, aborting" );
   263 	exit(3);
   264     }
   266     XVisualInfo *visual = video_glx_get_visual();
   267     GdkVisual *gdkvis = gdk_x11_screen_lookup_visual( gtk_widget_get_screen(win->window), visual->visualid );
   268     GdkColormap *colormap = gdk_colormap_new( gdkvis, FALSE );
   269     win->video = gtk_drawing_area_new();
   270     gtk_widget_set_colormap( win->video, colormap );
   271     GTK_WIDGET_SET_FLAGS(win->video, GTK_CAN_FOCUS|GTK_CAN_DEFAULT);
   272     gtk_widget_set_size_request( win->video, 640, 480 ); 
   273     gtk_widget_set_double_buffered( win->video, FALSE );
   274     frame = gtk_frame_new(NULL);
   275     gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_IN );
   276     gtk_container_add( GTK_CONTAINER(frame), win->video );
   278     win->statusbar = gtk_statusbar_new();
   280     vbox = gtk_vbox_new(FALSE, 0);
   281     gtk_container_add( GTK_CONTAINER(win->window), vbox );
   282     gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, FALSE, 0 );
   283     gtk_box_pack_start( GTK_BOX(vbox), toolbar, FALSE, FALSE, 0 );
   284     gtk_box_pack_start( GTK_BOX(vbox), frame, TRUE, TRUE, 0 );
   285     gtk_box_pack_start( GTK_BOX(vbox), win->statusbar, FALSE, FALSE, 0 );
   286     gtk_widget_show_all( win->window );
   287     gtk_widget_grab_focus( win->video );
   289     gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, "Stopped" );
   291     g_signal_connect( win->window, "delete_event", 
   292 		      G_CALLBACK(on_main_window_deleted), win );
   293     g_signal_connect( win->window, "window-state-event",
   294 		      G_CALLBACK(on_main_window_state_changed), win );
   296     g_signal_connect( win->video, "grab-broken-event",
   297 		      G_CALLBACK(on_video_window_grab_broken), 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 );
   318     return win;
   319 }
   321 void main_window_set_status_text( main_window_t win, char *text )
   322 {
   323     gtk_statusbar_pop( GTK_STATUSBAR(win->statusbar), 1 );
   324     if( win->is_grabbed ) {
   325 	char buf[128];
   326 	snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <ctrl><alt> to release grab)") );
   327 	gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, buf );
   328     } else {
   329 	gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, text );
   330     }
   331 }
   333 void main_window_set_running( main_window_t win, gboolean running )
   334 {
   335     char *text = running ? _("Running") : _("Stopped");
   336     gtk_gui_enable_action( "Pause", running );
   337     gtk_gui_enable_action( "Run", !running && dreamcast_can_run() );
   338     main_window_set_status_text( win, text );
   339 }
   341 void main_window_set_framerate( main_window_t win, float rate )
   342 {
   345 }
   347 void main_window_set_speed( main_window_t win, double speed )
   348 {
   349     char buf[32];
   351     snprintf( buf, 32, "Running (%2.4f%%)", speed );
   352     main_window_set_status_text( win, buf );
   353 }
   355 GtkWidget *main_window_get_renderarea( main_window_t win )
   356 {
   357     return win->video;
   358 }
   360 GtkWindow *main_window_get_frame( main_window_t win )
   361 {
   362     return GTK_WINDOW(win->window);
   363 }
   365 void main_window_set_fullscreen( main_window_t win, gboolean fullscreen )
   366 {
   367     if( fullscreen ) {
   368 	gtk_window_fullscreen( GTK_WINDOW(win->window) );
   369     } else {
   370 	gtk_window_unfullscreen( GTK_WINDOW(win->window) );
   371     }
   372 }
   374 void main_window_set_use_grab( main_window_t win, gboolean use_grab )
   375 {
   376     if( use_grab != win->use_grab ) {
   377 	if( use_grab ) {
   378 	    GdkCursor *cursor = gdk_cursor_new( GDK_HAND2 );
   379 	    gdk_window_set_cursor( win->video->window, cursor );
   380 	    gdk_cursor_unref( cursor );
   381 	} else {
   382 	    gdk_window_set_cursor( win->video->window, NULL );
   383 	    if( gdk_pointer_is_grabbed() ) {
   384 		video_window_ungrab_display(win);
   385 	    }
   386 	}
   387 	win->use_grab = use_grab;
   388     }
   389 }
.