Search
lxdream.org :: lxdream/src/gtkui/gtk_win.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gtkui/gtk_win.c
changeset 1017:f94af28e38b7
prev1015:ad448bedc48a
next1065:bc1cc0c54917
author nkeynes
date Thu Jun 25 21:21:18 2009 +0000 (12 years ago)
permissions -rw-r--r--
last change Add quick state bits to the menus
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();
   426     if( disc == NULL ) {
   427         gtk_window_set_title( GTK_WINDOW(win->window), lxdream_package_name );
   428     } else {
   429         char buf[256];
   430         snprintf( buf, sizeof(buf), "%s - %s", lxdream_package_name, disc );
   431         gtk_window_set_title( GTK_WINDOW(win->window), buf );
   432     }
   433 }
.