nkeynes@435: /** nkeynes@561: * $Id$ nkeynes@435: * nkeynes@435: * Define the main (emu) GTK window, along with its menubars, nkeynes@435: * toolbars, etc. nkeynes@435: * nkeynes@435: * Copyright (c) 2005 Nathan Keynes. nkeynes@435: * nkeynes@435: * This program is free software; you can redistribute it and/or modify nkeynes@435: * it under the terms of the GNU General Public License as published by nkeynes@435: * the Free Software Foundation; either version 2 of the License, or nkeynes@435: * (at your option) any later version. nkeynes@435: * nkeynes@435: * This program is distributed in the hope that it will be useful, nkeynes@435: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@435: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@435: * GNU General Public License for more details. nkeynes@435: */ nkeynes@435: nkeynes@435: #include nkeynes@435: #include nkeynes@435: #include nkeynes@435: #include nkeynes@435: #include nkeynes@435: #include nkeynes@435: #include nkeynes@435: nkeynes@435: #include nkeynes@608: #include nkeynes@608: #include nkeynes@545: #include nkeynes@435: nkeynes@658: #include "lxdream.h" nkeynes@669: #include "dreamcast.h" nkeynes@669: #include "display.h" nkeynes@839: #include "gdrom/gdrom.h" nkeynes@537: #include "gtkui/gtkui.h" nkeynes@887: #include "drivers/video_gl.h" nkeynes@658: nkeynes@435: nkeynes@435: struct main_window_info { nkeynes@435: GtkWidget *window; nkeynes@435: GtkWidget *video; nkeynes@486: GtkWidget *menubar; nkeynes@486: GtkWidget *toolbar; nkeynes@435: GtkWidget *statusbar; nkeynes@435: GtkActionGroup *actions; nkeynes@608: gboolean use_grab; nkeynes@608: gboolean is_grabbed; nkeynes@608: int32_t mouse_x, mouse_y; nkeynes@435: }; nkeynes@435: nkeynes@608: nkeynes@608: /******************** Video window **************************/ nkeynes@608: nkeynes@839: #if !(GTK_CHECK_VERSION(2,8,0)) nkeynes@839: void gdk_display_warp_pointer (GdkDisplay *display, nkeynes@839: GdkScreen *screen, nkeynes@839: gint x, nkeynes@839: gint y); nkeynes@839: #endif nkeynes@839: nkeynes@608: /** nkeynes@608: * Adjust the mouse pointer so that it appears in the center of the video nkeynes@608: * window. Mainly used for when we have the mouse grab nkeynes@608: */ nkeynes@608: void video_window_center_pointer( main_window_t win ) nkeynes@608: { nkeynes@608: GdkDisplay *display = gtk_widget_get_display(win->video); nkeynes@608: GdkScreen *screen = gtk_widget_get_screen(win->video); nkeynes@608: int x,y; nkeynes@608: int width, height; nkeynes@608: nkeynes@608: gdk_window_get_origin(win->video->window, &x, &y); nkeynes@608: gdk_drawable_get_size(GDK_DRAWABLE(win->video->window), &width, &height); nkeynes@608: x += width / 2; nkeynes@608: y += height / 2; nkeynes@736: nkeynes@608: gdk_display_warp_pointer( display, screen, x, y ); nkeynes@608: win->mouse_x = width/2; nkeynes@608: win->mouse_y = height/2; nkeynes@608: } nkeynes@608: nkeynes@608: /** nkeynes@608: * Grab the keyboard and mouse for the display. The mouse cursor is hidden and nkeynes@608: * moved to the centre of the window. nkeynes@608: * nkeynes@608: * @param win The window receiving the grab nkeynes@608: * @return TRUE if the grab was successful, FALSE on failure. nkeynes@608: */ nkeynes@608: gboolean video_window_grab_display( main_window_t win ) nkeynes@608: { nkeynes@608: GdkWindow *gdkwin = win->video->window; nkeynes@608: GdkColor color = { 0,0,0,0 }; nkeynes@608: char bytes[32]; /* 16 * 16 / 8 */ nkeynes@608: memset(bytes, 0, 32); nkeynes@608: GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bytes, 16, 16); nkeynes@608: GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &color, &color, 16, 16); nkeynes@608: gdk_pixmap_unref(pixmap); nkeynes@608: nkeynes@608: gboolean success = nkeynes@736: gdk_pointer_grab( gdkwin, FALSE, nkeynes@736: GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK, nkeynes@736: gdkwin, cursor, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS; nkeynes@608: gdk_cursor_unref(cursor); nkeynes@608: if( success ) { nkeynes@736: success = gdk_keyboard_grab( gdkwin, FALSE, GDK_CURRENT_TIME ) == GDK_GRAB_SUCCESS; nkeynes@736: if( !success ) { nkeynes@736: gdk_pointer_ungrab(GDK_CURRENT_TIME); nkeynes@736: } nkeynes@608: } nkeynes@608: win->is_grabbed = success; nkeynes@608: main_window_set_running(win, dreamcast_is_running()); nkeynes@608: return success; nkeynes@608: } nkeynes@608: nkeynes@608: /** nkeynes@608: * Release the display grab. nkeynes@608: */ nkeynes@608: void video_window_ungrab_display( main_window_t win ) nkeynes@608: { nkeynes@608: gdk_pointer_ungrab(GDK_CURRENT_TIME); nkeynes@608: gdk_keyboard_ungrab(GDK_CURRENT_TIME); nkeynes@608: win->is_grabbed = FALSE; nkeynes@608: main_window_set_running(win, dreamcast_is_running()); nkeynes@608: } nkeynes@608: nkeynes@608: static gboolean on_video_window_mouse_motion( GtkWidget *widget, GdkEventMotion *event, nkeynes@736: gpointer user_data ) nkeynes@608: { nkeynes@608: main_window_t win = (main_window_t)user_data; nkeynes@854: int x = (int)event->x; nkeynes@854: int y = (int)event->y; nkeynes@608: if( win->is_grabbed && nkeynes@736: (x != win->mouse_x || y != win->mouse_y) ) { nkeynes@849: input_event_mousemove( x - win->mouse_x, y - win->mouse_y, FALSE ); nkeynes@736: video_window_center_pointer(win); nkeynes@849: } else { nkeynes@850: int width, height; nkeynes@854: gl_window_to_system_coords( &x, &y ); nkeynes@854: input_event_mousemove( x, y, TRUE ); nkeynes@608: } nkeynes@608: return TRUE; nkeynes@608: } nkeynes@608: nkeynes@850: static gboolean on_video_window_mouse_exited( GtkWidget *widget, GdkEventCrossing *event, nkeynes@850: gpointer user_data ) nkeynes@850: { nkeynes@850: main_window_t win = (main_window_t)user_data; nkeynes@850: if( !win->is_grabbed ) { nkeynes@850: input_event_mousemove( -1, -1, TRUE ); nkeynes@850: } nkeynes@850: return TRUE; nkeynes@850: } nkeynes@850: nkeynes@608: static gboolean on_video_window_mouse_pressed( GtkWidget *widget, GdkEventButton *event, nkeynes@736: gpointer user_data ) nkeynes@608: { nkeynes@608: main_window_t win = (main_window_t)user_data; nkeynes@608: if( win->is_grabbed ) { nkeynes@849: input_event_mousedown( event->button-1, 0, 0, FALSE ); nkeynes@839: } else { nkeynes@854: int x = (int)event->x; nkeynes@854: int y = (int)event->y; nkeynes@854: gl_window_to_system_coords( &x, &y ); nkeynes@854: input_event_mousedown( event->button-1, x, y, TRUE ); nkeynes@608: } nkeynes@608: return TRUE; nkeynes@608: } nkeynes@608: nkeynes@608: static gboolean on_video_window_mouse_released( GtkWidget *widget, GdkEventButton *event, nkeynes@736: gpointer user_data ) nkeynes@608: { nkeynes@608: main_window_t win = (main_window_t)user_data; nkeynes@608: if( win->is_grabbed ) { nkeynes@849: input_event_mouseup( event->button-1, 0, 0, FALSE ); nkeynes@608: } else if( win->use_grab) { nkeynes@736: video_window_grab_display(win); nkeynes@839: } else { nkeynes@854: int x = (int)event->x; nkeynes@854: int y = (int)event->y; nkeynes@854: gl_window_to_system_coords( &x, &y ); nkeynes@854: input_event_mouseup( event->button-1, x, y, TRUE ); nkeynes@839: } nkeynes@608: return TRUE; nkeynes@608: } nkeynes@608: nkeynes@608: static gboolean on_video_window_key_pressed( GtkWidget *widget, GdkEventKey *event, nkeynes@736: gpointer user_data ) nkeynes@608: { nkeynes@608: main_window_t win = (main_window_t)user_data; nkeynes@608: if( win->is_grabbed ) { nkeynes@668: #ifdef HAVE_GTK_OSX nkeynes@736: /* On OSX, use the command key rather than ctrl-alt. Mainly because GTK/OSX nkeynes@736: * doesn't seem to be able to get ctrl-alt reliably nkeynes@736: **/ nkeynes@736: if( event->keyval == GDK_Meta_L || event->keyval == GDK_Meta_R ) { nkeynes@736: video_window_ungrab_display(win); nkeynes@736: return TRUE; nkeynes@736: } nkeynes@668: #else nkeynes@736: /* Check for ungrab key combo (ctrl-alt). Unfortunately GDK sends it as nkeynes@736: * a singly-modified keypress rather than a double-modified 'null' press, nkeynes@736: * so we have to do a little more work. nkeynes@736: * Only check Ctrl/Shift/Alt for state - don't want to check numlock/capslock/ nkeynes@736: * mouse buttons/etc nkeynes@736: */ nkeynes@618: int mod = gdk_keycode_to_modifier(gtk_widget_get_display(widget), event->hardware_keycode); nkeynes@736: int state = event->state & gtk_accelerator_get_default_mod_mask(); nkeynes@736: if( (state == GDK_CONTROL_MASK && mod == GDK_MOD1_MASK) || nkeynes@736: (state == GDK_MOD1_MASK && mod == GDK_CONTROL_MASK) ) { nkeynes@736: video_window_ungrab_display(win); nkeynes@736: // Consume the keypress, DC doesn't get it. nkeynes@736: return TRUE; nkeynes@736: } nkeynes@668: #endif nkeynes@608: } nkeynes@614: input_event_keydown( NULL, gtk_get_unmodified_keyval(event), 1 ); nkeynes@608: return TRUE; nkeynes@608: } nkeynes@608: nkeynes@608: static gboolean on_video_window_key_released( GtkWidget *widget, GdkEventKey *event, nkeynes@736: gpointer user_data ) nkeynes@608: { nkeynes@614: input_event_keyup( NULL, gtk_get_unmodified_keyval(event), 0 ); nkeynes@608: return TRUE; nkeynes@608: } nkeynes@608: nkeynes@614: static gboolean on_video_window_focus_changed( GtkWidget *widget, GdkEventFocus *event, nkeynes@736: gpointer user_data ) nkeynes@614: { nkeynes@614: display_set_focused(event->in); nkeynes@669: return TRUE; nkeynes@614: } nkeynes@614: nkeynes@608: /*************************** Main window (frame) ******************************/ nkeynes@608: nkeynes@486: static gboolean on_main_window_deleted( GtkWidget *widget, GdkEvent event, gpointer user_data ) nkeynes@457: { nkeynes@671: dreamcast_shutdown(); nkeynes@457: exit(0); nkeynes@457: } nkeynes@457: nkeynes@486: static void on_main_window_state_changed( GtkWidget *widget, GdkEventWindowState *state, nkeynes@736: gpointer userdata ) nkeynes@486: { nkeynes@497: main_window_t win = (main_window_t)userdata; nkeynes@486: if( state->changed_mask & GDK_WINDOW_STATE_FULLSCREEN ) { nkeynes@736: gboolean fs = (state->new_window_state & GDK_WINDOW_STATE_FULLSCREEN); nkeynes@736: GtkWidget *frame = gtk_widget_get_parent(win->video); nkeynes@736: if( frame->style == NULL ) { nkeynes@736: gtk_widget_set_style( frame, gtk_style_new() ); nkeynes@736: } nkeynes@736: if( fs ) { nkeynes@736: gtk_widget_hide( win->menubar ); nkeynes@736: gtk_widget_hide( win->toolbar ); nkeynes@736: gtk_widget_hide( win->statusbar ); nkeynes@736: nkeynes@736: frame->style->xthickness = 0; nkeynes@736: frame->style->ythickness = 0; nkeynes@736: } else { nkeynes@736: frame->style->xthickness = 2; nkeynes@736: frame->style->ythickness = 2; nkeynes@736: gtk_widget_show( win->menubar ); nkeynes@736: gtk_widget_show( win->toolbar ); nkeynes@736: gtk_widget_show( win->statusbar ); nkeynes@736: } nkeynes@736: gtk_widget_queue_draw( win->window ); nkeynes@486: } nkeynes@486: } nkeynes@486: nkeynes@455: main_window_t main_window_new( const gchar *title, GtkWidget *menubar, GtkWidget *toolbar, nkeynes@736: GtkAccelGroup *accel_group ) nkeynes@435: { nkeynes@435: GtkWidget *vbox; nkeynes@437: GtkWidget *frame; nkeynes@435: main_window_t win = g_malloc0( sizeof(struct main_window_info) ); nkeynes@435: nkeynes@435: win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); nkeynes@486: win->menubar = menubar; nkeynes@486: win->toolbar = toolbar; nkeynes@608: win->use_grab = FALSE; nkeynes@608: win->is_grabbed = FALSE; nkeynes@435: gtk_window_set_title( GTK_WINDOW(win->window), title ); nkeynes@435: gtk_window_add_accel_group (GTK_WINDOW (win->window), accel_group); nkeynes@435: nkeynes@455: gtk_toolbar_set_style( GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS ); nkeynes@658: nkeynes@659: win->video = video_gtk_create_drawable(); nkeynes@435: gtk_widget_set_size_request( win->video, 640, 480 ); nkeynes@608: gtk_widget_set_double_buffered( win->video, FALSE ); nkeynes@437: frame = gtk_frame_new(NULL); nkeynes@437: gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_IN ); nkeynes@437: gtk_container_add( GTK_CONTAINER(frame), win->video ); nkeynes@437: nkeynes@435: win->statusbar = gtk_statusbar_new(); nkeynes@435: nkeynes@435: vbox = gtk_vbox_new(FALSE, 0); nkeynes@435: gtk_container_add( GTK_CONTAINER(win->window), vbox ); nkeynes@455: gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, FALSE, 0 ); nkeynes@455: gtk_box_pack_start( GTK_BOX(vbox), toolbar, FALSE, FALSE, 0 ); nkeynes@437: gtk_box_pack_start( GTK_BOX(vbox), frame, TRUE, TRUE, 0 ); nkeynes@435: gtk_box_pack_start( GTK_BOX(vbox), win->statusbar, FALSE, FALSE, 0 ); nkeynes@435: gtk_widget_show_all( win->window ); nkeynes@435: gtk_widget_grab_focus( win->video ); nkeynes@736: nkeynes@437: gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, "Stopped" ); nkeynes@608: nkeynes@457: g_signal_connect( win->window, "delete_event", nkeynes@736: G_CALLBACK(on_main_window_deleted), win ); nkeynes@486: g_signal_connect( win->window, "window-state-event", nkeynes@736: G_CALLBACK(on_main_window_state_changed), win ); nkeynes@608: nkeynes@608: g_signal_connect( win->video, "key-press-event", nkeynes@736: G_CALLBACK(on_video_window_key_pressed), win ); nkeynes@608: g_signal_connect( win->video, "key-release-event", nkeynes@736: G_CALLBACK(on_video_window_key_released), win ); nkeynes@608: g_signal_connect( win->video, "motion-notify-event", nkeynes@736: G_CALLBACK(on_video_window_mouse_motion), win ); nkeynes@850: g_signal_connect( win->video, "leave-notify-event", nkeynes@850: G_CALLBACK(on_video_window_mouse_exited), win ); nkeynes@608: g_signal_connect( win->video, "button-press-event", nkeynes@736: G_CALLBACK(on_video_window_mouse_pressed), win ); nkeynes@608: g_signal_connect( win->video, "button-release-event", nkeynes@736: G_CALLBACK(on_video_window_mouse_released), win ); nkeynes@614: g_signal_connect( win->video, "focus-in-event", nkeynes@736: G_CALLBACK(on_video_window_focus_changed), win); nkeynes@614: g_signal_connect( win->video, "focus-out-event", nkeynes@736: G_CALLBACK(on_video_window_focus_changed), win); nkeynes@608: nkeynes@608: gtk_widget_add_events( win->video, nkeynes@736: GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | nkeynes@736: GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | nkeynes@850: GDK_POINTER_MOTION_MASK | GDK_FOCUS_CHANGE_MASK | nkeynes@850: GDK_LEAVE_NOTIFY_MASK ); nkeynes@608: nkeynes@435: return win; nkeynes@435: } nkeynes@435: nkeynes@887: void main_window_set_status_text( main_window_t win, const char *text ) nkeynes@608: { nkeynes@608: gtk_statusbar_pop( GTK_STATUSBAR(win->statusbar), 1 ); nkeynes@608: if( win->is_grabbed ) { nkeynes@736: char buf[128]; nkeynes@668: #ifdef HAVE_GTK_OSX nkeynes@736: snprintf( buf, sizeof(buf), "%s %s", text, _("(Press to release grab)") ); nkeynes@668: #else nkeynes@736: snprintf( buf, sizeof(buf), "%s %s", text, _("(Press to release grab)") ); nkeynes@668: #endif nkeynes@736: gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, buf ); nkeynes@608: } else { nkeynes@736: gtk_statusbar_push( GTK_STATUSBAR(win->statusbar), 1, text ); nkeynes@608: } nkeynes@608: } nkeynes@608: nkeynes@435: void main_window_set_running( main_window_t win, gboolean running ) nkeynes@435: { nkeynes@887: const char *text = running ? _("Running") : _("Stopped"); nkeynes@455: gtk_gui_enable_action( "Pause", running ); nkeynes@889: gtk_gui_enable_action( "Run", !running ); nkeynes@608: main_window_set_status_text( win, text ); nkeynes@435: } nkeynes@435: nkeynes@435: void main_window_set_framerate( main_window_t win, float rate ) nkeynes@435: { nkeynes@435: nkeynes@435: nkeynes@435: } nkeynes@435: nkeynes@437: void main_window_set_speed( main_window_t win, double speed ) nkeynes@437: { nkeynes@437: char buf[32]; nkeynes@437: nkeynes@480: snprintf( buf, 32, "Running (%2.4f%%)", speed ); nkeynes@608: main_window_set_status_text( win, buf ); nkeynes@437: } nkeynes@437: nkeynes@435: GtkWidget *main_window_get_renderarea( main_window_t win ) nkeynes@435: { nkeynes@435: return win->video; nkeynes@435: } nkeynes@447: nkeynes@447: GtkWindow *main_window_get_frame( main_window_t win ) nkeynes@447: { nkeynes@447: return GTK_WINDOW(win->window); nkeynes@447: } nkeynes@486: nkeynes@486: void main_window_set_fullscreen( main_window_t win, gboolean fullscreen ) nkeynes@486: { nkeynes@486: if( fullscreen ) { nkeynes@736: gtk_window_fullscreen( GTK_WINDOW(win->window) ); nkeynes@486: } else { nkeynes@736: gtk_window_unfullscreen( GTK_WINDOW(win->window) ); nkeynes@486: } nkeynes@486: } nkeynes@608: nkeynes@608: void main_window_set_use_grab( main_window_t win, gboolean use_grab ) nkeynes@608: { nkeynes@608: if( use_grab != win->use_grab ) { nkeynes@736: if( use_grab ) { nkeynes@736: GdkCursor *cursor = gdk_cursor_new( GDK_HAND2 ); nkeynes@736: gdk_window_set_cursor( win->video->window, cursor ); nkeynes@736: gdk_cursor_unref( cursor ); nkeynes@736: } else { nkeynes@736: gdk_window_set_cursor( win->video->window, NULL ); nkeynes@736: if( gdk_pointer_is_grabbed() ) { nkeynes@736: video_window_ungrab_display(win); nkeynes@736: } nkeynes@736: } nkeynes@736: win->use_grab = use_grab; nkeynes@608: } nkeynes@608: } nkeynes@837: nkeynes@837: void main_window_update_title( main_window_t win ) nkeynes@837: { nkeynes@837: const char *disc = gdrom_get_current_disc_title(); nkeynes@837: nkeynes@837: if( disc == NULL ) { nkeynes@837: gtk_window_set_title( GTK_WINDOW(win->window), lxdream_package_name ); nkeynes@837: } else { nkeynes@837: char buf[256]; nkeynes@837: snprintf( buf, sizeof(buf), "%s - %s", lxdream_package_name, disc ); nkeynes@837: gtk_window_set_title( GTK_WINDOW(win->window), buf ); nkeynes@837: } nkeynes@843: }