Search
lxdream.org :: lxdream/src/gtkui/gtk_win.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/gtkui/gtk_win.c
changeset 1066:ddffe9d2b332
prev635:76c63aac3590
prev1065:bc1cc0c54917
next1098:4f2750753a6c
author nkeynes
date Sun Jul 05 13:53:48 2009 +1000 (11 years ago)
permissions -rw-r--r--
last change No-op merge lxdream-render to remove head (actually long since merged)
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/gtkui/gtk_win.c Sun Jul 05 13:53:48 2009 +1000
1.3 @@ -0,0 +1,433 @@
1.4 +/**
1.5 + * $Id$
1.6 + *
1.7 + * Define the main (emu) GTK window, along with its menubars,
1.8 + * toolbars, etc.
1.9 + *
1.10 + * Copyright (c) 2005 Nathan Keynes.
1.11 + *
1.12 + * This program is free software; you can redistribute it and/or modify
1.13 + * it under the terms of the GNU General Public License as published by
1.14 + * the Free Software Foundation; either version 2 of the License, or
1.15 + * (at your option) any later version.
1.16 + *
1.17 + * This program is distributed in the hope that it will be useful,
1.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.20 + * GNU General Public License for more details.
1.21 + */
1.22 +
1.23 +#include <assert.h>
1.24 +#include <sys/types.h>
1.25 +#include <sys/stat.h>
1.26 +#include <unistd.h>
1.27 +#include <string.h>
1.28 +#include <stdio.h>
1.29 +#include <stdlib.h>
1.30 +
1.31 +#include <X11/Xlib.h>
1.32 +#include <X11/Xutil.h>
1.33 +#include <gtk/gtk.h>
1.34 +#include <gdk/gdk.h>
1.35 +#include <gdk/gdkkeysyms.h>
1.36 +
1.37 +#include "lxdream.h"
1.38 +#include "dreamcast.h"
1.39 +#include "display.h"
1.40 +#include "gdrom/gdrom.h"
1.41 +#include "gtkui/gtkui.h"
1.42 +#include "drivers/video_gl.h"
1.43 +
1.44 +
1.45 +struct main_window_info {
1.46 + GtkWidget *window;
1.47 + GtkWidget *video;
1.48 + GtkWidget *menubar;
1.49 + GtkWidget *toolbar;
1.50 + GtkWidget *statusbar;
1.51 + GtkActionGroup *actions;
1.52 + gboolean use_grab;
1.53 + gboolean is_grabbed;
1.54 + int32_t mouse_x, mouse_y;
1.55 +};
1.56 +
1.57 +
1.58 +/******************** Video window **************************/
1.59 +
1.60 +#if !(GTK_CHECK_VERSION(2,8,0))
1.61 +void gdk_display_warp_pointer (GdkDisplay *display,
1.62 + GdkScreen *screen,
1.63 + gint x,
1.64 + gint y);
1.65 +#endif
1.66 +
1.67 +/**
1.68 + * Adjust the mouse pointer so that it appears in the center of the video
1.69 + * window. Mainly used for when we have the mouse grab
1.70 + */
1.71 +void video_window_center_pointer( main_window_t win )
1.72 +{
1.73 + GdkDisplay *display = gtk_widget_get_display(win->video);
1.74 + GdkScreen *screen = gtk_widget_get_screen(win->video);
1.75 + int x,y;
1.76 + int width, height;
1.77 +
1.78 + gdk_window_get_origin(win->video->window, &x, &y);
1.79 + gdk_drawable_get_size(GDK_DRAWABLE(win->video->window), &width, &height);
1.80 + x += width / 2;
1.81 + y += height / 2;
1.82 +
1.83 + gdk_display_warp_pointer( display, screen, x, y );
1.84 + win->mouse_x = width/2;
1.85 + win->mouse_y = height/2;
1.86 +}
1.87 +
1.88 +/**
1.89 + * Grab the keyboard and mouse for the display. The mouse cursor is hidden and
1.90 + * moved to the centre of the window.
1.91 + *
1.92 + * @param win The window receiving the grab
1.93 + * @return TRUE if the grab was successful, FALSE on failure.
1.94 + */
1.95 +gboolean video_window_grab_display( main_window_t win )
1.96 +{
1.97 + GdkWindow *gdkwin = win->video->window;
1.98 + GdkColor color = { 0,0,0,0 };
1.99 + char bytes[32]; /* 16 * 16 / 8 */
1.100 + memset(bytes, 0, 32);
1.101 + GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bytes, 16, 16);
1.102 + GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &color, &color, 16, 16);
1.103 + gdk_pixmap_unref(pixmap);
1.104 +
1.105 + gboolean success =
1.106 + gdk_pointer_grab( gdkwin, FALSE,
1.107 + GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK,
1.108 + gdkwin, cursor, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
1.109 + gdk_cursor_unref(cursor);
1.110 + if( success ) {
1.111 + success = gdk_keyboard_grab( gdkwin, FALSE, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS;
1.112 + if( !success ) {
1.113 + gdk_pointer_ungrab(GDK_CURRENT_TIME);
1.114 + }
1.115 + }
1.116 + win->is_grabbed = success;
1.117 + main_window_set_running(win, dreamcast_is_running());
1.118 + return success;
1.119 +}
1.120 +
1.121 +/**
1.122 + * Release the display grab.
1.123 + */
1.124 +void video_window_ungrab_display( main_window_t win )
1.125 +{
1.126 + gdk_pointer_ungrab(GDK_CURRENT_TIME);
1.127 + gdk_keyboard_ungrab(GDK_CURRENT_TIME);
1.128 + win->is_grabbed = FALSE;
1.129 + main_window_set_running(win, dreamcast_is_running());
1.130 +}
1.131 +
1.132 +static gboolean on_video_window_mouse_motion( GtkWidget *widget, GdkEventMotion *event,
1.133 + gpointer user_data )
1.134 +{
1.135 + main_window_t win = (main_window_t)user_data;
1.136 + int x = (int)event->x;
1.137 + int y = (int)event->y;
1.138 + if( win->is_grabbed &&
1.139 + (x != win->mouse_x || y != win->mouse_y) ) {
1.140 + input_event_mousemove( x - win->mouse_x, y - win->mouse_y, FALSE );
1.141 + video_window_center_pointer(win);
1.142 + } else {
1.143 + int width, height;
1.144 + gl_window_to_system_coords( &x, &y );
1.145 + input_event_mousemove( x, y, TRUE );
1.146 + }
1.147 + return TRUE;
1.148 +}
1.149 +
1.150 +static gboolean on_video_window_mouse_exited( GtkWidget *widget, GdkEventCrossing *event,
1.151 + gpointer user_data )
1.152 +{
1.153 + main_window_t win = (main_window_t)user_data;
1.154 + if( !win->is_grabbed ) {
1.155 + input_event_mousemove( -1, -1, TRUE );
1.156 + }
1.157 + return TRUE;
1.158 +}
1.159 +
1.160 +static gboolean on_video_window_mouse_pressed( GtkWidget *widget, GdkEventButton *event,
1.161 + gpointer user_data )
1.162 +{
1.163 + main_window_t win = (main_window_t)user_data;
1.164 + if( win->is_grabbed ) {
1.165 + input_event_mousedown( event->button-1, 0, 0, FALSE );
1.166 + } else {
1.167 + int x = (int)event->x;
1.168 + int y = (int)event->y;
1.169 + gl_window_to_system_coords( &x, &y );
1.170 + input_event_mousedown( event->button-1, x, y, TRUE );
1.171 + }
1.172 + return TRUE;
1.173 +}
1.174 +
1.175 +static gboolean on_video_window_mouse_released( GtkWidget *widget, GdkEventButton *event,
1.176 + gpointer user_data )
1.177 +{
1.178 + main_window_t win = (main_window_t)user_data;
1.179 + if( win->is_grabbed ) {
1.180 + input_event_mouseup( event->button-1, 0, 0, FALSE );
1.181 + } else if( win->use_grab) {
1.182 + video_window_grab_display(win);
1.183 + } else {
1.184 + int x = (int)event->x;
1.185 + int y = (int)event->y;
1.186 + gl_window_to_system_coords( &x, &y );
1.187 + input_event_mouseup( event->button-1, x, y, TRUE );
1.188 + }
1.189 + return TRUE;
1.190 +}
1.191 +
1.192 +static gboolean on_video_window_key_pressed( GtkWidget *widget, GdkEventKey *event,
1.193 + gpointer user_data )
1.194 +{
1.195 + main_window_t win = (main_window_t)user_data;
1.196 + if( win->is_grabbed ) {
1.197 +#ifdef HAVE_GTK_OSX
1.198 + /* On OSX, use the command key rather than ctrl-alt. Mainly because GTK/OSX
1.199 + * doesn't seem to be able to get ctrl-alt reliably
1.200 + **/
1.201 + if( event->keyval == GDK_Meta_L || event->keyval == GDK_Meta_R ) {
1.202 + video_window_ungrab_display(win);
1.203 + return TRUE;
1.204 + }
1.205 +#else
1.206 + /* Check for ungrab key combo (ctrl-alt). Unfortunately GDK sends it as
1.207 + * a singly-modified keypress rather than a double-modified 'null' press,
1.208 + * so we have to do a little more work.
1.209 + * Only check Ctrl/Shift/Alt for state - don't want to check numlock/capslock/
1.210 + * mouse buttons/etc
1.211 + */
1.212 + int mod = gdk_keycode_to_modifier(gtk_widget_get_display(widget), event->hardware_keycode);
1.213 + int state = event->state & gtk_accelerator_get_default_mod_mask();
1.214 + if( (state == GDK_CONTROL_MASK && mod == GDK_MOD1_MASK) ||
1.215 + (state == GDK_MOD1_MASK && mod == GDK_CONTROL_MASK) ) {
1.216 + video_window_ungrab_display(win);
1.217 + // Consume the keypress, DC doesn't get it.
1.218 + return TRUE;
1.219 + }
1.220 +#endif
1.221 + }
1.222 + input_event_keydown( NULL, gtk_get_unmodified_keyval(event), MAX_PRESSURE );
1.223 + return TRUE;
1.224 +}
1.225 +
1.226 +static gboolean on_video_window_key_released( GtkWidget *widget, GdkEventKey *event,
1.227 + gpointer user_data )
1.228 +{
1.229 + input_event_keyup( NULL, gtk_get_unmodified_keyval(event) );
1.230 + return TRUE;
1.231 +}
1.232 +
1.233 +static gboolean on_video_window_focus_changed( GtkWidget *widget, GdkEventFocus *event,
1.234 + gpointer user_data )
1.235 +{
1.236 + display_set_focused(event->in);
1.237 + return TRUE;
1.238 +}
1.239 +
1.240 +/*************************** Main window (frame) ******************************/
1.241 +
1.242 +static gboolean on_main_window_deleted( GtkWidget *widget, GdkEvent event, gpointer user_data )
1.243 +{
1.244 + dreamcast_shutdown();
1.245 + exit(0);
1.246 +}
1.247 +
1.248 +static void on_main_window_state_changed( GtkWidget *widget, GdkEventWindowState *state,
1.249 + gpointer userdata )
1.250 +{
1.251 + main_window_t win = (main_window_t)userdata;
1.252 + if( state->changed_mask & GDK_WINDOW_STATE_FULLSCREEN ) {
1.253 + gboolean fs = (state->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
1.254 + main_window_show_gui(win, fs);
1.255 + }
1.256 +}
1.257 +
1.258 +void main_window_show_gui(main_window_t win, gboolean fullscreen)
1.259 +{
1.260 + GtkWidget *frame = gtk_widget_get_parent(win->video);
1.261 + if( frame->style == NULL ) {
1.262 + gtk_widget_set_style( frame, gtk_style_new() );
1.263 + }
1.264 + if( fullscreen ) {
1.265 + gtk_widget_hide( win->menubar );
1.266 + gtk_widget_hide( win->toolbar );
1.267 + gtk_widget_hide( win->statusbar );
1.268 +
1.269 + frame->style->xthickness = 0;
1.270 + frame->style->ythickness = 0;
1.271 + } else {
1.272 + frame->style->xthickness = 2;
1.273 + frame->style->ythickness = 2;
1.274 + gtk_widget_show( win->menubar );
1.275 + gtk_widget_show( win->toolbar );
1.276 + gtk_widget_show( win->statusbar );
1.277 + }
1.278 + gtk_widget_queue_draw( win->window );
1.279 +}
1.280 +
1.281 +main_window_t main_window_new( const gchar *title, GtkWidget *menubar, GtkWidget *toolbar,
1.282 + GtkAccelGroup *accel_group )
1.283 +{
1.284 + GtkWidget *vbox;
1.285 + GtkWidget *frame;
1.286 + main_window_t win = g_malloc0( sizeof(struct main_window_info) );
1.287 +
1.288 + win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1.289 + win->menubar = menubar;
1.290 + win->toolbar = toolbar;
1.291 + win->use_grab = FALSE;
1.292 + win->is_grabbed = FALSE;
1.293 + gtk_window_set_title( GTK_WINDOW(win->window), title );
1.294 + gtk_window_add_accel_group (GTK_WINDOW (win->window), accel_group);
1.295 + gtk_window_set_icon_from_file( GTK_WINDOW(win->window),
1.296 + PACKAGE_DATA_DIR "/pixmaps/lxdream.png", NULL );
1.297 +
1.298 + gtk_toolbar_set_style( GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS );
1.299 +
1.300 + win->video = video_gtk_create_drawable();
1.301 + gtk_widget_set_size_request( win->video, 640, 480 );
1.302 + gtk_widget_set_double_buffered( win->video, FALSE );
1.303 + frame = gtk_frame_new(NULL);
1.304 + gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_IN );
1.305 + gtk_container_add( GTK_CONTAINER(frame), win->video );
1.306 +
1.307 + win->statusbar = gtk_statusbar_new();
1.308 +
1.309 + vbox = gtk_vbox_new(FALSE, 0);
1.310 + gtk_container_add( GTK_CONTAINER(win->window), vbox );
1.311 + gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, FALSE, 0 );
1.312 + gtk_box_pack_start( GTK_BOX(vbox), toolbar, FALSE, FALSE, 0 );
1.313 + gtk_box_pack_start( GTK_BOX(vbox), frame, TRUE, TRUE, 0 );
1.314 + gtk_box_pack_start( GTK_BOX(vbox), win->statusbar, FALSE, FALSE, 0 );
1.315 + gtk_widget_show_all( win->window );
1.316 + gtk_widget_grab_focus( win->video );
1.317 +
1.318 + gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, "Stopped" );
1.319 +
1.320 + g_signal_connect( win->window, "delete_event",
1.321 + G_CALLBACK(on_main_window_deleted), win );
1.322 + g_signal_connect( win->window, "window-state-event",
1.323 + G_CALLBACK(on_main_window_state_changed), win );
1.324 +
1.325 + g_signal_connect( win->video, "key-press-event",
1.326 + G_CALLBACK(on_video_window_key_pressed), win );
1.327 + g_signal_connect( win->video, "key-release-event",
1.328 + G_CALLBACK(on_video_window_key_released), win );
1.329 + g_signal_connect( win->video, "motion-notify-event",
1.330 + G_CALLBACK(on_video_window_mouse_motion), win );
1.331 + g_signal_connect( win->video, "leave-notify-event",
1.332 + G_CALLBACK(on_video_window_mouse_exited), win );
1.333 + g_signal_connect( win->video, "button-press-event",
1.334 + G_CALLBACK(on_video_window_mouse_pressed), win );
1.335 + g_signal_connect( win->video, "button-release-event",
1.336 + G_CALLBACK(on_video_window_mouse_released), win );
1.337 + g_signal_connect( win->video, "focus-in-event",
1.338 + G_CALLBACK(on_video_window_focus_changed), win);
1.339 + g_signal_connect( win->video, "focus-out-event",
1.340 + G_CALLBACK(on_video_window_focus_changed), win);
1.341 +
1.342 + gtk_widget_add_events( win->video,
1.343 + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
1.344 + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1.345 + GDK_POINTER_MOTION_MASK | GDK_FOCUS_CHANGE_MASK |
1.346 + GDK_LEAVE_NOTIFY_MASK );
1.347 +
1.348 + return win;
1.349 +}
1.350 +
1.351 +void main_window_set_status_text( main_window_t win, const char *text )
1.352 +{
1.353 + gtk_statusbar_pop( GTK_STATUSBAR(win->statusbar), 1 );
1.354 + if( win->is_grabbed ) {
1.355 + char buf[128];
1.356 +#ifdef HAVE_GTK_OSX
1.357 + snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <command> to release grab)") );
1.358 +#else
1.359 + snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <ctrl><alt> to release grab)") );
1.360 +#endif
1.361 + gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, buf );
1.362 + } else {
1.363 + gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, text );
1.364 + }
1.365 +}
1.366 +
1.367 +void main_window_set_running( main_window_t win, gboolean running )
1.368 +{
1.369 + const char *text = running ? _("Running") : _("Stopped");
1.370 + gtk_gui_enable_action( "Pause", running );
1.371 + gtk_gui_enable_action( "Run", !running );
1.372 + main_window_set_status_text( win, text );
1.373 +}
1.374 +
1.375 +void main_window_set_framerate( main_window_t win, float rate )
1.376 +{
1.377 +
1.378 +
1.379 +}
1.380 +
1.381 +void main_window_set_speed( main_window_t win, double speed )
1.382 +{
1.383 + char buf[32];
1.384 +
1.385 + snprintf( buf, 32, "Running (%2.4f%%)", speed );
1.386 + main_window_set_status_text( win, buf );
1.387 +}
1.388 +
1.389 +GtkWidget *main_window_get_renderarea( main_window_t win )
1.390 +{
1.391 + return win->video;
1.392 +}
1.393 +
1.394 +GtkWindow *main_window_get_frame( main_window_t win )
1.395 +{
1.396 + return GTK_WINDOW(win->window);
1.397 +}
1.398 +
1.399 +void main_window_set_fullscreen( main_window_t win, gboolean fullscreen )
1.400 +{
1.401 + if( fullscreen ) {
1.402 + gtk_window_fullscreen( GTK_WINDOW(win->window) );
1.403 + } else {
1.404 + gtk_window_unfullscreen( GTK_WINDOW(win->window) );
1.405 + }
1.406 +}
1.407 +
1.408 +void main_window_set_use_grab( main_window_t win, gboolean use_grab )
1.409 +{
1.410 + if( use_grab != win->use_grab ) {
1.411 + if( use_grab ) {
1.412 + GdkCursor *cursor = gdk_cursor_new( GDK_HAND2 );
1.413 + gdk_window_set_cursor( win->video->window, cursor );
1.414 + gdk_cursor_unref( cursor );
1.415 + } else {
1.416 + gdk_window_set_cursor( win->video->window, NULL );
1.417 + if( gdk_pointer_is_grabbed() ) {
1.418 + video_window_ungrab_display(win);
1.419 + }
1.420 + }
1.421 + win->use_grab = use_grab;
1.422 + }
1.423 +}
1.424 +
1.425 +void main_window_update_title( main_window_t win )
1.426 +{
1.427 + const char *disc = gdrom_get_current_disc_title();
1.428 +
1.429 + if( disc == NULL ) {
1.430 + gtk_window_set_title( GTK_WINDOW(win->window), lxdream_package_name );
1.431 + } else {
1.432 + char buf[256];
1.433 + snprintf( buf, sizeof(buf), "%s - %s", lxdream_package_name, disc );
1.434 + gtk_window_set_title( GTK_WINDOW(win->window), buf );
1.435 + }
1.436 +}
.