/* * QEMU GTK display driver * * Copyright (c) 2004 Sebastien Bechet * Copyright (c) 2003 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* TODO : configure !i386-softmmu grab RGBA endian problem in vga.c fullscreen gtk key release event ok, remove modifiers_state, reset_keys ... */ #include "vl.h" #include #include GtkWidget *pWindow; GtkImage *pImage; GdkPixbuf *pPixbuf; static int mouse_dx, mouse_dy, mouse_dz, mouse_buttons; static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ static int last_vm_running; static int gui_saved_grab; static int gui_fullscreen; static int gui_key_modifier_pressed; static int gui_keysym; static int gui_fullscreen_initial_grab; static int gui_grab_code = GDK_CONTROL_MASK | GDK_MOD1_MASK; static uint8_t modifiers_state[256]; static void gtk_update(DisplayState *ds, int x, int y, int w, int h) { GdkRectangle grect; GdkRegion *pRegion; grect.x = (gint)x; grect.y = (gint)y; grect.width = (gint)w; grect.height = (gint)h; pRegion = gdk_region_rectangle(&grect); gdk_window_invalidate_region(GTK_WIDGET(pImage)->window,pRegion,TRUE); gdk_region_destroy(pRegion); } static void gtk_resize(DisplayState *ds, int w, int h) { if (pPixbuf != NULL) g_object_unref(pPixbuf); pPixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,TRUE, 8, w, h); gtk_image_set_from_pixbuf(pImage,pPixbuf); gtk_window_resize(GTK_WINDOW(pWindow), w, h); if (gui_fullscreen) gtk_window_fullscreen(GTK_WINDOW(pWindow)); else gtk_window_unfullscreen(GTK_WINDOW(pWindow)); ds->data = gdk_pixbuf_get_pixels(pPixbuf); ds->linesize = gdk_pixbuf_get_rowstride(pPixbuf); ds->depth = gdk_pixbuf_get_n_channels(pPixbuf)*gdk_pixbuf_get_bits_per_sample(pPixbuf); //4*8; ds->width = gdk_pixbuf_get_width(pPixbuf); ds->height = gdk_pixbuf_get_height(pPixbuf); } /* generic keyboard conversion */ #include "gtk_keysym.h" #include "keymaps.c" static kbd_layout_t *kbd_layout = NULL; static uint8_t gtk_keyevent_to_keycode_generic(const GdkEventKey *ev) { int keysym; /* workaround for X11+SDL bug with AltGR */ keysym = ev->keyval; /* XXX : ? if (keysym == 0 && ev->keysym.scancode == 113) keysym = SDLK_MODE;*/ return keysym2scancode(kbd_layout, keysym); } /* specific keyboard conversions from scan codes */ #if defined(_WIN32) static uint8_t gtk_keyevent_to_keycode(const GdkEventKey *ev) { return ev->hardware_keycode; } #else static const uint8_t x_keycode_to_pc_keycode[61] = { 0xc7, /* 97 Home */ 0xc8, /* 98 Up */ 0xc9, /* 99 PgUp */ 0xcb, /* 100 Left */ 0x4c, /* 101 KP-5 */ 0xcd, /* 102 Right */ 0xcf, /* 103 End */ 0xd0, /* 104 Down */ 0xd1, /* 105 PgDn */ 0xd2, /* 106 Ins */ 0xd3, /* 107 Del */ 0x9c, /* 108 Enter */ 0x9d, /* 109 Ctrl-R */ 0x0, /* 110 Pause */ 0xb7, /* 111 Print */ 0xb5, /* 112 Divide */ 0xb8, /* 113 Alt-R */ 0xc6, /* 114 Break */ 0x0, /* 115 */ 0x0, /* 116 */ 0x0, /* 117 */ 0x0, /* 118 */ 0x0, /* 119 */ 0x70, /* 120 Hiragana_Katakana */ 0x0, /* 121 */ 0x0, /* 122 */ 0x73, /* 123 backslash */ 0x0, /* 124 */ 0x0, /* 125 */ 0x0, /* 126 */ 0x0, /* 127 */ 0x0, /* 128 */ 0x79, /* 129 Henkan */ 0x0, /* 130 */ 0x7b, /* 131 Muhenkan */ 0x0, /* 132 */ 0x7d, /* 133 Yen */ 0x0, /* 134 */ 0x0, /* 135 */ 0x47, /* 136 KP_7 */ 0x48, /* 137 KP_8 */ 0x49, /* 138 KP_9 */ 0x4b, /* 139 KP_4 */ 0x4c, /* 140 KP_5 */ 0x4d, /* 141 KP_6 */ 0x4f, /* 142 KP_1 */ 0x50, /* 143 KP_2 */ 0x51, /* 144 KP_3 */ 0x52, /* 145 KP_0 */ 0x53, /* 146 KP_. */ 0x47, /* 147 KP_HOME */ 0x48, /* 148 KP_UP */ 0x49, /* 149 KP_PgUp */ 0x4b, /* 150 KP_Left */ 0x4c, /* 151 KP_ */ 0x4d, /* 152 KP_Right */ 0x4f, /* 153 KP_End */ 0x50, /* 154 KP_Down */ 0x51, /* 155 KP_PgDn */ 0x52, /* 156 KP_Ins */ 0x53, /* 157 KP_Del */ }; static uint8_t gtk_keyevent_to_keycode(const GdkEventKey *ev) { int keycode; keycode = ev->hardware_keycode; if (keycode < 9) { keycode = 0; } else if (keycode < 97) { keycode -= 8; /* just an offset */ } else if (keycode < 158) { /* use conversion table */ keycode = x_keycode_to_pc_keycode[keycode - 97]; } else { keycode = 0; } return keycode; } #endif static void reset_keys(void) { int i; for(i = 0; i < 256; i++) { if (modifiers_state[i]) { if (i & 0x80) kbd_put_keycode(0xe0); kbd_put_keycode(i | 0x80); modifiers_state[i] = 0; } } } static void gtk_process_key(GdkEventKey *ev) { int keycode, v; /* Pause key */ if (ev->hardware_keycode == 0x6E) { /* specific case */ v = 0; if (ev->type == GDK_KEY_RELEASE) v |= 0x80; kbd_put_keycode(0xe1); kbd_put_keycode(0x1d | v); kbd_put_keycode(0x45 | v); return; } if (kbd_layout) { keycode = gtk_keyevent_to_keycode_generic(ev); } else { keycode = gtk_keyevent_to_keycode(ev); } switch(keycode) { case 0x00: /* sent when leaving window: reset the modifiers state */ reset_keys(); return; case 0x32: /* Left Shift */ case 0x3E: /* Right Shift */ case 0x25: /* Left CTRL */ case 0x6D: /* Right CTRL */ case 0x40: /* Left ALT */ case 0x71: /* Right ALT */ if (ev->type == GDK_KEY_RELEASE) modifiers_state[keycode] = 0; else modifiers_state[keycode] = 1; break; case 0x45: /* num lock */ case 0x3a: /* caps lock */ /*XXX : GTK send the key up event... */ kbd_put_keycode(keycode); kbd_put_keycode(keycode | 0x80); return; } /* now send the key code */ if (keycode & 0x80) kbd_put_keycode(0xe0); if (ev->type == GDK_KEY_RELEASE) kbd_put_keycode(keycode | 0x80); else kbd_put_keycode(keycode & 0x7f); } static void gtk_update_caption(void) { char buf[1024]; strcpy(buf, "QEMU"); if (!vm_running) { strcat(buf, " [Stopped]"); } if (gui_grab) { strcat(buf, " - Press Ctrl-Alt to exit grab"); } gtk_window_set_title(GTK_WINDOW(pWindow), buf); } static void gtk_grab_start(void) { /* showcursor(false),grabinput*/ /* gdk_pointer_grab(pWindow->window, TRUE, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK, pWindow->window, NULL, GDK_CURRENT_TIME);*/ gui_grab = 1; gtk_update_caption(); } static void gtk_grab_end(void) { /* gdk_pointer_ungrab(GDK_CURRENT_TIME);*/ gui_grab = 0; gtk_update_caption(); } static void gtk_send_mouse_event(void) { fprintf(stderr,"dx=%d, dy=%d dz=%d buttons=%d\n",mouse_dx, mouse_dy, mouse_dz, mouse_buttons); kbd_mouse_event(mouse_dx, mouse_dy, mouse_dz, mouse_buttons); } static void toggle_full_screen(DisplayState *ds) { gui_fullscreen = !gui_fullscreen; if (gui_fullscreen) { gui_saved_grab = gui_grab; gtk_grab_start(); } else { if (!gui_saved_grab) gtk_grab_end(); } vga_invalidate_display(); vga_update_display(); } static void gtk_refresh(DisplayState *ds) { if (last_vm_running != vm_running) { last_vm_running = vm_running; gtk_update_caption(); } if (is_active_console(vga_console)) vga_update_display(); if (gtk_events_pending ()) { gtk_main_iteration_do (FALSE); } // fprintf(stderr,"gtk_refresh d=%d, w=%d, h=%d, ls=%d\n",ds->depth,ds->width,ds->height,ds->linesize); } static gboolean on_key(GtkWidget *widget, GdkEventKey *ev, DisplayState *ds) { int mod_state; fprintf(stderr,"on_key : se=%02X state=%02X key=0x%02X hkey=0x%02X grp=%d\n",ev->send_event,ev->state,ev->keyval,ev->hardware_keycode,ev->group); if (ev->type == GDK_KEY_PRESS) { mod_state = (ev->state & gui_grab_code) == gui_grab_code; gui_key_modifier_pressed = mod_state; if (gui_key_modifier_pressed) { int keycode; keycode = ev->hardware_keycode; switch(keycode) { case 0x29: /* 'f' hardware key */ toggle_full_screen(ds); gui_keysym = 1; break; case 0x0a ... 0x12: /* '1' to '9' keys */ console_select(keycode - 0x0a); if (is_active_console(vga_console)) { /* tell the vga console to redisplay itself */ vga_invalidate_display(); } else { if (gui_grab) gtk_grab_end(); } gui_keysym = 1; break; default: break; } } else if (!is_active_console(vga_console)) { int keysym; keysym = 0; if (ev->state & GDK_CONTROL_MASK) { switch(ev->hardware_keycode) { case 0x62: keysym = QEMU_KEY_CTRL_UP; break; case 0x68: keysym = QEMU_KEY_CTRL_DOWN; break; case 0x64: keysym = QEMU_KEY_CTRL_LEFT; break; case 0x66: keysym = QEMU_KEY_CTRL_RIGHT; break; case 0x61: keysym = QEMU_KEY_CTRL_HOME; break; case 0x67: keysym = QEMU_KEY_CTRL_END; break; case 0x63: keysym = QEMU_KEY_CTRL_PAGEUP; break; case 0x69: keysym = QEMU_KEY_CTRL_PAGEDOWN; break; default: break; } } else { switch(ev->hardware_keycode) { case 0x62: keysym = QEMU_KEY_UP; break; case 0x68: keysym = QEMU_KEY_DOWN; break; case 0x64: keysym = QEMU_KEY_LEFT; break; case 0x66: keysym = QEMU_KEY_RIGHT; break; case 0x61: keysym = QEMU_KEY_HOME; break; case 0x67: keysym = QEMU_KEY_END; break; case 0x63: keysym = QEMU_KEY_PAGEUP; break; case 0x69: keysym = QEMU_KEY_PAGEDOWN; break; case 0x16: keysym = QEMU_KEY_BACKSPACE; break; case 0x6b: keysym = QEMU_KEY_DELETE; break; default: break; } } if (keysym) { kbd_put_keysym(keysym); } else { kbd_put_keysym(ev->keyval); } } } else if (ev->type == GDK_KEY_RELEASE) { mod_state = (ev->state & gui_grab_code); if (!mod_state) { if (gui_key_modifier_pressed) { if (gui_keysym == 0) { /* exit/enter grab if pressing Ctrl-Alt */ if (!gui_grab) gtk_grab_start(); else gtk_grab_end(); reset_keys(); return TRUE; } gui_key_modifier_pressed = 0; gui_keysym = 0; } } } if (is_active_console(vga_console)) gtk_process_key(ev); return TRUE; } static gboolean on_motion(GtkWidget *widget, GdkEventMotion *event, DisplayState *ds) { if (gui_grab) { mouse_dx = event->x; mouse_dy = event->y; gtk_send_mouse_event(); } return TRUE; } #ifdef SDL_BUTTON_WHEELUP static gboolean on_scroll(GtkWidget *widget, GdkEventScroll *event, DisplayState *ds) { if (event->direction == GDK_SCROLL_UP) mouse_dz--; if (event->direction == GDK_SCROLL_DOWN) mouse_dz++; gtk_send_mouse_event(); return TRUE; } #endif static gboolean on_button(GtkWidget *widget, GdkEventButton *event, DisplayState *ds) { mouse_dx = event->x; mouse_dy = event->y; if (!gui_grab) { if (event->type == GDK_BUTTON_PRESS && event->button == 1) { gtk_grab_start(); } } else { if (event->button == 1) mouse_buttons ^= MOUSE_EVENT_LBUTTON; else if (event->button == 2) mouse_buttons ^= MOUSE_EVENT_MBUTTON; else if (event->button == 3) mouse_buttons ^= MOUSE_EVENT_RBUTTON; gtk_send_mouse_event(); } return TRUE; } static void on_quit_clicked() { fprintf(stderr,"click\n"); } void gtk_display_init(DisplayState *ds, int full_screen) { mouse_dx = mouse_dy = mouse_dz = mouse_buttons = 0; #if defined(__APPLE__) /* always use generic keymaps */ if (!keyboard_layout) keyboard_layout = "en-us"; #endif if(keyboard_layout) { kbd_layout = init_keyboard_layout(keyboard_layout); if (!kbd_layout) exit(1); } gtk_init(NULL,NULL); pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); pImage = GTK_IMAGE(gtk_image_new()); pPixbuf = NULL; ds->dpy_update = gtk_update; ds->dpy_resize = gtk_resize; ds->dpy_refresh = gtk_refresh; gtk_container_add(GTK_CONTAINER(pWindow), GTK_WIDGET(pImage)); gtk_resize(ds, 640, 400); gtk_update_caption(); g_signal_connect(pWindow,"delete-event", G_CALLBACK(on_quit_clicked), NULL); g_signal_connect(pWindow,"key-press-event", G_CALLBACK(on_key), ds); g_signal_connect(pWindow,"key-release-event", G_CALLBACK(on_key), ds); g_signal_connect(pWindow,"motion-notify-event", G_CALLBACK(on_motion), ds); g_signal_connect(pWindow,"button-press-event", G_CALLBACK(on_button), ds); g_signal_connect(pWindow,"button-release-event", G_CALLBACK(on_button), ds); #ifdef SDL_BUTTON_WHEELUP g_signal_connect(pWindow,"scroll-event", G_CALLBACK(on_scroll), ds); gtk_widget_add_events(pWindow,GDK_SCROLL_MASK); #endif gtk_widget_add_events(pWindow,GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); gtk_widget_show_all(pWindow); if (full_screen) { gui_fullscreen = 1; gui_fullscreen_initial_grab = 1; gtk_grab_start(); } }