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 608:4f588e52bce0
prev606:23029426ab8f
next612:410b48e63d53
author nkeynes
date Sat Jan 26 02:45:27 2008 +0000 (12 years ago)
permissions -rw-r--r--
last change Bug #50: Implement mouse and keyboard
file annotate diff log raw
nkeynes@435
     1
/**
nkeynes@561
     2
 * $Id$
nkeynes@435
     3
 *
nkeynes@435
     4
 * Define the main (emu) GTK window, along with its menubars,
nkeynes@435
     5
 * toolbars, etc.
nkeynes@435
     6
 *
nkeynes@435
     7
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@435
     8
 *
nkeynes@435
     9
 * This program is free software; you can redistribute it and/or modify
nkeynes@435
    10
 * it under the terms of the GNU General Public License as published by
nkeynes@435
    11
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@435
    12
 * (at your option) any later version.
nkeynes@435
    13
 *
nkeynes@435
    14
 * This program is distributed in the hope that it will be useful,
nkeynes@435
    15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@435
    16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@435
    17
 * GNU General Public License for more details.
nkeynes@435
    18
 */
nkeynes@435
    19
nkeynes@435
    20
#include <assert.h>
nkeynes@435
    21
#include <sys/types.h>
nkeynes@435
    22
#include <sys/stat.h>
nkeynes@435
    23
#include <unistd.h>
nkeynes@435
    24
#include <string.h>
nkeynes@435
    25
#include <stdio.h>
nkeynes@435
    26
#include <stdlib.h>
nkeynes@435
    27
nkeynes@435
    28
#include <gtk/gtk.h>
nkeynes@608
    29
#include <gdk/gdk.h>
nkeynes@545
    30
#include <gdk/gdkx.h>
nkeynes@608
    31
#include <gdk/gdkkeysyms.h>
nkeynes@545
    32
#include <X11/Xutil.h>
nkeynes@435
    33
nkeynes@435
    34
#include "dream.h"
nkeynes@537
    35
#include "gtkui/gtkui.h"
nkeynes@545
    36
#include "drivers/video_glx.h"
nkeynes@435
    37
nkeynes@435
    38
nkeynes@435
    39
struct main_window_info {
nkeynes@435
    40
    GtkWidget *window;
nkeynes@435
    41
    GtkWidget *video;
nkeynes@486
    42
    GtkWidget *menubar;
nkeynes@486
    43
    GtkWidget *toolbar;
nkeynes@435
    44
    GtkWidget *statusbar;
nkeynes@435
    45
    GtkActionGroup *actions;
nkeynes@608
    46
    gboolean use_grab;
nkeynes@608
    47
    gboolean is_grabbed;
nkeynes@608
    48
    int32_t mouse_x, mouse_y;
nkeynes@435
    49
};
nkeynes@435
    50
nkeynes@608
    51
nkeynes@608
    52
/******************** Video window **************************/
nkeynes@608
    53
nkeynes@608
    54
/**
nkeynes@608
    55
 * Adjust the mouse pointer so that it appears in the center of the video
nkeynes@608
    56
 * window. Mainly used for when we have the mouse grab
nkeynes@608
    57
 */
nkeynes@608
    58
void video_window_center_pointer( main_window_t win )
nkeynes@608
    59
{
nkeynes@608
    60
    GdkDisplay *display = gtk_widget_get_display(win->video);
nkeynes@608
    61
    GdkScreen *screen = gtk_widget_get_screen(win->video);
nkeynes@608
    62
    int x,y;
nkeynes@608
    63
    int width, height;
nkeynes@608
    64
nkeynes@608
    65
    gdk_window_get_origin(win->video->window, &x, &y);
nkeynes@608
    66
    gdk_drawable_get_size(GDK_DRAWABLE(win->video->window), &width, &height);
nkeynes@608
    67
    x += width / 2;
nkeynes@608
    68
    y += height / 2;
nkeynes@608
    69
    
nkeynes@608
    70
    gdk_display_warp_pointer( display, screen, x, y );
nkeynes@608
    71
    win->mouse_x = width/2;
nkeynes@608
    72
    win->mouse_y = height/2;
nkeynes@608
    73
}
nkeynes@608
    74
nkeynes@608
    75
/**
nkeynes@608
    76
 * Grab the keyboard and mouse for the display. The mouse cursor is hidden and
nkeynes@608
    77
 * moved to the centre of the window.
nkeynes@608
    78
 *
nkeynes@608
    79
 * @param win The window receiving the grab
nkeynes@608
    80
 * @return TRUE if the grab was successful, FALSE on failure.
nkeynes@608
    81
 */
nkeynes@608
    82
gboolean video_window_grab_display( main_window_t win )
nkeynes@608
    83
{
nkeynes@608
    84
    GdkWindow *gdkwin = win->video->window;
nkeynes@608
    85
    GdkColor color = { 0,0,0,0 };
nkeynes@608
    86
    char bytes[32]; /* 16 * 16 / 8 */
nkeynes@608
    87
    memset(bytes, 0, 32);
nkeynes@608
    88
    GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bytes, 16, 16);
nkeynes@608
    89
    GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &color, &color, 16, 16);
nkeynes@608
    90
    gdk_pixmap_unref(pixmap);
nkeynes@608
    91
nkeynes@608
    92
    gboolean success =
nkeynes@608
    93
	gdk_pointer_grab( gdkwin, FALSE, 
nkeynes@608
    94
			  GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, 
nkeynes@608
    95
			  gdkwin, cursor, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
nkeynes@608
    96
    gdk_cursor_unref(cursor);
nkeynes@608
    97
    if( success ) {
nkeynes@608
    98
	success = gdk_keyboard_grab( gdkwin, FALSE, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
nkeynes@608
    99
	if( !success ) {
nkeynes@608
   100
	    gdk_pointer_ungrab(GDK_CURRENT_TIME);
nkeynes@608
   101
	}
nkeynes@608
   102
    }
nkeynes@608
   103
    win->is_grabbed = success;
nkeynes@608
   104
    main_window_set_running(win, dreamcast_is_running());
nkeynes@608
   105
    return success;
nkeynes@608
   106
}
nkeynes@608
   107
nkeynes@608
   108
/**
nkeynes@608
   109
 * Release the display grab.
nkeynes@608
   110
 */
nkeynes@608
   111
void video_window_ungrab_display( main_window_t win )
nkeynes@608
   112
{
nkeynes@608
   113
    gdk_pointer_ungrab(GDK_CURRENT_TIME);
nkeynes@608
   114
    gdk_keyboard_ungrab(GDK_CURRENT_TIME);
nkeynes@608
   115
    win->is_grabbed = FALSE;
nkeynes@608
   116
    main_window_set_running(win, dreamcast_is_running());
nkeynes@608
   117
}
nkeynes@608
   118
nkeynes@608
   119
static gboolean on_video_window_mouse_motion( GtkWidget *widget, GdkEventMotion *event,
nkeynes@608
   120
					      gpointer user_data )
nkeynes@608
   121
{
nkeynes@608
   122
    main_window_t win = (main_window_t)user_data;
nkeynes@608
   123
    int32_t x = (int32_t)event->x;
nkeynes@608
   124
    int32_t y = (int32_t)event->y;
nkeynes@608
   125
    if( win->is_grabbed && 
nkeynes@608
   126
	(x != win->mouse_x || y != win->mouse_y) ) {
nkeynes@608
   127
	uint32_t buttons = (event->state >> 8)&0x1F;
nkeynes@608
   128
	input_event_mouse( buttons, x - win->mouse_x, y - win->mouse_y );
nkeynes@608
   129
	video_window_center_pointer(win);
nkeynes@608
   130
    }
nkeynes@608
   131
    return TRUE;
nkeynes@608
   132
}
nkeynes@608
   133
nkeynes@608
   134
static gboolean on_video_window_mouse_pressed( GtkWidget *widget, GdkEventButton *event,
nkeynes@608
   135
					       gpointer user_data )
nkeynes@608
   136
{
nkeynes@608
   137
    main_window_t win = (main_window_t)user_data;
nkeynes@608
   138
    if( win->is_grabbed ) {
nkeynes@608
   139
	// Get the buttons from the event state, and remove the released button
nkeynes@608
   140
	uint32_t buttons = ((event->state >> 8) & 0x1F) | (1<<(event->button-1));
nkeynes@608
   141
	input_event_mouse( buttons, 0, 0 );
nkeynes@608
   142
    }
nkeynes@608
   143
    return TRUE;
nkeynes@608
   144
}
nkeynes@608
   145
nkeynes@608
   146
static gboolean on_video_window_mouse_released( GtkWidget *widget, GdkEventButton *event,
nkeynes@608
   147
					      gpointer user_data )
nkeynes@608
   148
{
nkeynes@608
   149
    main_window_t win = (main_window_t)user_data;
nkeynes@608
   150
    if( win->is_grabbed ) {
nkeynes@608
   151
	// Get the buttons from the event state, and remove the released button
nkeynes@608
   152
	uint32_t buttons = ((event->state >> 8) & 0x1F) & (~(1<<(event->button-1)));
nkeynes@608
   153
	input_event_mouse( buttons, 0, 0 );
nkeynes@608
   154
    } else if( win->use_grab) {
nkeynes@608
   155
	video_window_grab_display(win);
nkeynes@608
   156
    }
nkeynes@608
   157
    return TRUE;
nkeynes@608
   158
}
nkeynes@608
   159
nkeynes@608
   160
static gboolean on_video_window_key_pressed( GtkWidget *widget, GdkEventKey *event,
nkeynes@608
   161
					    gpointer user_data )
nkeynes@608
   162
{
nkeynes@608
   163
    main_window_t win = (main_window_t)user_data;
nkeynes@608
   164
    if( win->is_grabbed ) {
nkeynes@608
   165
	/* Check for ungrab key combo (ctrl-alt). Unfortunately GDK sends it as
nkeynes@608
   166
	 * a singly-modified keypress rather than a double-modified 'null' press, 
nkeynes@608
   167
	 * so we have to do a little more work.
nkeynes@608
   168
	 */
nkeynes@608
   169
	if( (event->state == GDK_CONTROL_MASK &&
nkeynes@608
   170
	     (event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R)) ||
nkeynes@608
   171
	    (event->state == GDK_MOD1_MASK &&
nkeynes@608
   172
	     (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R)) ) {
nkeynes@608
   173
	    video_window_ungrab_display(win);
nkeynes@608
   174
	    // Consume the keypress, DC doesn't get it.
nkeynes@608
   175
	    return TRUE;
nkeynes@608
   176
	}
nkeynes@608
   177
    }
nkeynes@608
   178
    input_event_keydown( gtk_get_unmodified_keyval(event) );
nkeynes@608
   179
    return TRUE;
nkeynes@608
   180
}
nkeynes@608
   181
nkeynes@608
   182
static gboolean on_video_window_key_released( GtkWidget *widget, GdkEventKey *event,
nkeynes@608
   183
					     gpointer user_data )
nkeynes@608
   184
{
nkeynes@608
   185
    main_window_t win = (main_window_t)user_data;
nkeynes@608
   186
    input_event_keyup( gtk_get_unmodified_keyval(event) );
nkeynes@608
   187
    return TRUE;
nkeynes@608
   188
}
nkeynes@608
   189
nkeynes@608
   190
static gboolean on_video_window_grab_broken( GtkWidget *widget, GdkEventGrabBroken *event,
nkeynes@608
   191
					gpointer user_data )
nkeynes@608
   192
{
nkeynes@608
   193
    main_window_t win = (main_window_t)user_data;
nkeynes@608
   194
    fprintf( stderr, "Grab broken\n" );
nkeynes@608
   195
}
nkeynes@608
   196
nkeynes@608
   197
/*************************** Main window (frame) ******************************/
nkeynes@608
   198
nkeynes@486
   199
static gboolean on_main_window_deleted( GtkWidget *widget, GdkEvent event, gpointer user_data )
nkeynes@457
   200
{
nkeynes@457
   201
    exit(0);
nkeynes@457
   202
}
nkeynes@457
   203
nkeynes@486
   204
static void on_main_window_state_changed( GtkWidget *widget, GdkEventWindowState *state, 
nkeynes@486
   205
					  gpointer userdata )
nkeynes@486
   206
{
nkeynes@497
   207
    main_window_t win = (main_window_t)userdata;
nkeynes@486
   208
    if( state->changed_mask & GDK_WINDOW_STATE_FULLSCREEN ) {
nkeynes@486
   209
	gboolean fs = (state->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
nkeynes@497
   210
	GtkWidget *frame = gtk_widget_get_parent(win->video);
nkeynes@497
   211
	if( frame->style == NULL ) {
nkeynes@497
   212
	    gtk_widget_set_style( frame, gtk_style_new() );
nkeynes@497
   213
	}
nkeynes@497
   214
	if( fs ) {
nkeynes@497
   215
	    gtk_widget_hide( win->menubar );
nkeynes@497
   216
	    gtk_widget_hide( win->toolbar );
nkeynes@497
   217
	    gtk_widget_hide( win->statusbar );
nkeynes@497
   218
	    
nkeynes@497
   219
	    frame->style->xthickness = 0;
nkeynes@497
   220
	    frame->style->ythickness = 0;
nkeynes@497
   221
	} else {
nkeynes@497
   222
	    frame->style->xthickness = 2;
nkeynes@497
   223
	    frame->style->ythickness = 2;
nkeynes@497
   224
	    gtk_widget_show( win->menubar );
nkeynes@497
   225
	    gtk_widget_show( win->toolbar );
nkeynes@497
   226
	    gtk_widget_show( win->statusbar );
nkeynes@497
   227
	}
nkeynes@497
   228
	gtk_widget_queue_draw( win->window );
nkeynes@486
   229
    }
nkeynes@486
   230
}
nkeynes@486
   231
nkeynes@455
   232
main_window_t main_window_new( const gchar *title, GtkWidget *menubar, GtkWidget *toolbar,
nkeynes@455
   233
			       GtkAccelGroup *accel_group )
nkeynes@435
   234
{
nkeynes@435
   235
    GtkWidget *vbox;
nkeynes@437
   236
    GtkWidget *frame;
nkeynes@435
   237
    main_window_t win = g_malloc0( sizeof(struct main_window_info) );
nkeynes@435
   238
nkeynes@435
   239
    win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
nkeynes@486
   240
    win->menubar = menubar;
nkeynes@486
   241
    win->toolbar = toolbar;
nkeynes@608
   242
    win->use_grab = FALSE;
nkeynes@608
   243
    win->is_grabbed = FALSE;
nkeynes@435
   244
    gtk_window_set_title( GTK_WINDOW(win->window), title );
nkeynes@435
   245
    gtk_window_add_accel_group (GTK_WINDOW (win->window), accel_group);
nkeynes@435
   246
nkeynes@455
   247
    gtk_toolbar_set_style( GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS );
nkeynes@545
   248
    
nkeynes@545
   249
    Display *display = gdk_x11_display_get_xdisplay( gtk_widget_get_display(win->window));
nkeynes@545
   250
    Screen *screen = gdk_x11_screen_get_xscreen( gtk_widget_get_screen(win->window));
nkeynes@545
   251
    int screen_no = XScreenNumberOfScreen(screen);
nkeynes@606
   252
    if( !video_glx_init(display, screen_no) ) {
nkeynes@606
   253
        ERROR( "Unable to initialize GLX, aborting" );
nkeynes@606
   254
	exit(3);
nkeynes@606
   255
    }
nkeynes@435
   256
nkeynes@545
   257
    XVisualInfo *visual = video_glx_get_visual();
nkeynes@545
   258
    GdkVisual *gdkvis = gdk_x11_screen_lookup_visual( gtk_widget_get_screen(win->window), visual->visualid );
nkeynes@545
   259
    GdkColormap *colormap = gdk_colormap_new( gdkvis, FALSE );
nkeynes@435
   260
    win->video = gtk_drawing_area_new();
nkeynes@545
   261
    gtk_widget_set_colormap( win->video, colormap );
nkeynes@435
   262
    GTK_WIDGET_SET_FLAGS(win->video, GTK_CAN_FOCUS|GTK_CAN_DEFAULT);
nkeynes@435
   263
    gtk_widget_set_size_request( win->video, 640, 480 ); 
nkeynes@608
   264
    gtk_widget_set_double_buffered( win->video, FALSE );
nkeynes@437
   265
    frame = gtk_frame_new(NULL);
nkeynes@437
   266
    gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_IN );
nkeynes@437
   267
    gtk_container_add( GTK_CONTAINER(frame), win->video );
nkeynes@437
   268
nkeynes@435
   269
    win->statusbar = gtk_statusbar_new();
nkeynes@435
   270
nkeynes@435
   271
    vbox = gtk_vbox_new(FALSE, 0);
nkeynes@435
   272
    gtk_container_add( GTK_CONTAINER(win->window), vbox );
nkeynes@455
   273
    gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, FALSE, 0 );
nkeynes@455
   274
    gtk_box_pack_start( GTK_BOX(vbox), toolbar, FALSE, FALSE, 0 );
nkeynes@437
   275
    gtk_box_pack_start( GTK_BOX(vbox), frame, TRUE, TRUE, 0 );
nkeynes@435
   276
    gtk_box_pack_start( GTK_BOX(vbox), win->statusbar, FALSE, FALSE, 0 );
nkeynes@435
   277
    gtk_widget_show_all( win->window );
nkeynes@435
   278
    gtk_widget_grab_focus( win->video );
nkeynes@435
   279
    
nkeynes@437
   280
    gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, "Stopped" );
nkeynes@608
   281
nkeynes@457
   282
    g_signal_connect( win->window, "delete_event", 
nkeynes@457
   283
		      G_CALLBACK(on_main_window_deleted), win );
nkeynes@486
   284
    g_signal_connect( win->window, "window-state-event",
nkeynes@486
   285
		      G_CALLBACK(on_main_window_state_changed), win );
nkeynes@608
   286
nkeynes@608
   287
    g_signal_connect( win->video, "grab-broken-event",
nkeynes@608
   288
		      G_CALLBACK(on_video_window_grab_broken), win );
nkeynes@608
   289
    g_signal_connect( win->video, "key-press-event",
nkeynes@608
   290
		      G_CALLBACK(on_video_window_key_pressed), win );
nkeynes@608
   291
    g_signal_connect( win->video, "key-release-event",
nkeynes@608
   292
		      G_CALLBACK(on_video_window_key_released), win );
nkeynes@608
   293
    g_signal_connect( win->video, "motion-notify-event",
nkeynes@608
   294
		      G_CALLBACK(on_video_window_mouse_motion), win );
nkeynes@608
   295
    g_signal_connect( win->video, "button-press-event",
nkeynes@608
   296
		      G_CALLBACK(on_video_window_mouse_pressed), win );
nkeynes@608
   297
    g_signal_connect( win->video, "button-release-event", 
nkeynes@608
   298
		      G_CALLBACK(on_video_window_mouse_released), win );
nkeynes@608
   299
nkeynes@608
   300
    gtk_widget_add_events( win->video, 
nkeynes@608
   301
			   GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
nkeynes@608
   302
			   GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
nkeynes@608
   303
			   GDK_POINTER_MOTION_MASK );
nkeynes@608
   304
nkeynes@435
   305
    return win;
nkeynes@435
   306
}
nkeynes@435
   307
nkeynes@608
   308
void main_window_set_status_text( main_window_t win, char *text )
nkeynes@608
   309
{
nkeynes@608
   310
    gtk_statusbar_pop( GTK_STATUSBAR(win->statusbar), 1 );
nkeynes@608
   311
    if( win->is_grabbed ) {
nkeynes@608
   312
	char buf[128];
nkeynes@608
   313
	snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <ctrl><alt> to release grab)") );
nkeynes@608
   314
	gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, buf );
nkeynes@608
   315
    } else {
nkeynes@608
   316
	gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, text );
nkeynes@608
   317
    }
nkeynes@608
   318
}
nkeynes@608
   319
nkeynes@435
   320
void main_window_set_running( main_window_t win, gboolean running )
nkeynes@435
   321
{
nkeynes@608
   322
    char *text = running ? _("Running") : _("Stopped");
nkeynes@455
   323
    gtk_gui_enable_action( "Pause", running );
nkeynes@543
   324
    gtk_gui_enable_action( "Run", !running && dreamcast_can_run() );
nkeynes@608
   325
    main_window_set_status_text( win, text );
nkeynes@435
   326
}
nkeynes@435
   327
nkeynes@435
   328
void main_window_set_framerate( main_window_t win, float rate )
nkeynes@435
   329
{
nkeynes@435
   330
nkeynes@435
   331
nkeynes@435
   332
}
nkeynes@435
   333
nkeynes@437
   334
void main_window_set_speed( main_window_t win, double speed )
nkeynes@437
   335
{
nkeynes@437
   336
    char buf[32];
nkeynes@437
   337
nkeynes@480
   338
    snprintf( buf, 32, "Running (%2.4f%%)", speed );
nkeynes@608
   339
    main_window_set_status_text( win, buf );
nkeynes@437
   340
}
nkeynes@437
   341
nkeynes@435
   342
GtkWidget *main_window_get_renderarea( main_window_t win )
nkeynes@435
   343
{
nkeynes@435
   344
    return win->video;
nkeynes@435
   345
}
nkeynes@447
   346
nkeynes@447
   347
GtkWindow *main_window_get_frame( main_window_t win )
nkeynes@447
   348
{
nkeynes@447
   349
    return GTK_WINDOW(win->window);
nkeynes@447
   350
}
nkeynes@486
   351
nkeynes@486
   352
void main_window_set_fullscreen( main_window_t win, gboolean fullscreen )
nkeynes@486
   353
{
nkeynes@486
   354
    if( fullscreen ) {
nkeynes@486
   355
	gtk_window_fullscreen( GTK_WINDOW(win->window) );
nkeynes@486
   356
    } else {
nkeynes@486
   357
	gtk_window_unfullscreen( GTK_WINDOW(win->window) );
nkeynes@486
   358
    }
nkeynes@486
   359
}
nkeynes@608
   360
nkeynes@608
   361
void main_window_set_use_grab( main_window_t win, gboolean use_grab )
nkeynes@608
   362
{
nkeynes@608
   363
    if( use_grab != win->use_grab ) {
nkeynes@608
   364
	if( use_grab ) {
nkeynes@608
   365
	    GdkCursor *cursor = gdk_cursor_new( GDK_HAND2 );
nkeynes@608
   366
	    gdk_window_set_cursor( win->video->window, cursor );
nkeynes@608
   367
	    gdk_cursor_unref( cursor );
nkeynes@608
   368
	} else {
nkeynes@608
   369
	    gdk_window_set_cursor( win->video->window, NULL );
nkeynes@608
   370
	    if( gdk_pointer_is_grabbed() ) {
nkeynes@608
   371
		video_window_ungrab_display(win);
nkeynes@608
   372
	    }
nkeynes@608
   373
	}
nkeynes@608
   374
	win->use_grab = use_grab;
nkeynes@608
   375
    }
nkeynes@608
   376
}
.