From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KcUNi-0006n8-DF for qemu-devel@nongnu.org; Sun, 07 Sep 2008 20:13:26 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KcUNh-0006mn-0C for qemu-devel@nongnu.org; Sun, 07 Sep 2008 20:13:25 -0400 Received: from [199.232.76.173] (port=46573 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KcUNg-0006mk-Sf for qemu-devel@nongnu.org; Sun, 07 Sep 2008 20:13:24 -0400 Received: from an-out-0708.google.com ([209.85.132.243]:17374) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1KcUNg-0000iQ-Ak for qemu-devel@nongnu.org; Sun, 07 Sep 2008 20:13:24 -0400 Received: by an-out-0708.google.com with SMTP id d18so200350and.130 for ; Sun, 07 Sep 2008 17:13:24 -0700 (PDT) Message-ID: <48C46DF2.2050007@codemonkey.ws> Date: Sun, 07 Sep 2008 19:12:34 -0500 From: Anthony Liguori MIME-Version: 1.0 Subject: Re: [Qemu-devel] [PATCH] opengl rendering in the sdl window References: <48B81423.9050502@eu.citrix.com> <48BF4F4F.40208@codemonkey.ws> <20080904094226.GB11424@redhat.com> <48C3455E.8050906@codemonkey.ws> <20080907163123.GD31773@redhat.com> In-Reply-To: <20080907163123.GD31773@redhat.com> Content-Type: multipart/mixed; boundary="------------010205070000020007000701" Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: "Daniel P. Berrange" Cc: qemu-devel@nongnu.org This is a multi-part message in MIME format. --------------010205070000020007000701 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Daniel P. Berrange wrote: > On Sat, Sep 06, 2008 at 10:07:10PM -0500, Anthony Liguori wrote: > >> Daniel P. Berrange wrote: >> >>> On Wed, Sep 03, 2008 at 10:00:31PM -0500, Anthony Liguori wrote: >>> >>> Actually I'm not so sure this was a good idea in the end. I'm seriously >>> considering re-writing the GTK-VNC stuff to use Cairo, which in turn >>> can use 2-d hardware acceleration primitives - it really doesn't need >>> the full 3-d acceleration stack just for scaling. >>> >>> >> I tried to originally write the GTK-VNC scaling stuff in Cairo. Could >> not get it to perform well at all. I'd be really interested if you had >> better luck with it. >> > > I've just posted patches to the GTK-VNC devel list demonstrating use of > Cairo for all rendering. I can't notice any serious drop in performance > when enabling scaling with Cairo. If you confirm my tests, it'd be worth > evaluating Cairo as an alternative to OpenGL in QEMU too. > Here's the beginning of a GTK front-end in case someone wants to try adapting the Cairo patches to QEMU. Regards, Anthony Liguori > Regards, > Daniel > --------------010205070000020007000701 Content-Type: text/x-patch; name="gtk.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gtk.patch" diff --git a/Makefile b/Makefile index 0472e16..3927c40 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,9 @@ ifdef CONFIG_CURSES OBJS+=curses.o endif OBJS+=vnc.o d3des.o +ifdef CONFIG_GTK +OBJS+=gtk.o +endif ifdef CONFIG_COCOA OBJS+=cocoa.o @@ -145,6 +148,9 @@ cocoa.o: cocoa.m sdl.o: sdl.c keymaps.c sdl_keysym.h $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) -c -o $@ $< +gtk.o: gtk.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(GTK_CFLAGS) -c -o $@ $< + vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h $(CC) $(CFLAGS) $(CPPFLAGS) $(CONFIG_VNC_TLS_CFLAGS) -c -o $@ $< diff --git a/Makefile.target b/Makefile.target index 42162c3..572b44b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -515,6 +515,10 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) LIBS += $(CONFIG_VNC_TLS_LIBS) endif +ifdef CONFIG_GTK +LIBS += $(GTK_LIBS) +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 0a3b7c9..d0e63fa 100755 --- a/configure +++ b/configure @@ -90,6 +90,7 @@ EXESUF="" gdbstub="yes" slirp="yes" vde="yes" +gtk="yes" fmod_lib="" fmod_inc="" vnc_tls="yes" @@ -283,6 +284,8 @@ for opt do ;; --disable-vde) vde="no" ;; + --disable-gtk) gtk="no" + ;; --disable-kqemu) kqemu="no" ;; --disable-brlapi) brlapi="no" @@ -436,6 +439,7 @@ echo " --fmod-inc path to FMOD includes" echo " --enable-uname-release=R Return R for uname -r in usermode emulation" echo " --sparc_cpu=V Build qemu for Sparc architecture v7, v8, v8plus, v8plusa, v9" echo " --disable-vde disable support for vde network" +echo " --disable-gtk disable support for GTK" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -758,6 +762,20 @@ EOF fi fi +if test "$gtk" = "yes" ; then + cat > $TMPC < +int main(int argc, char **argv) { gtk_init(&argc, &argv); return 0; } +EOF + gtk_cflags=`pkg-config --cflags gtk+-2.0` + gtk_libs=`pkg-config --libs gtk+-2.0` + if $cc $ARCH_CFLAGS -o $TMPE $TMPC $gtk_cflags $gtk_libs ; then + : + else + gtk="no" + fi +fi + ########################################## # Sound support libraries probe @@ -923,6 +941,7 @@ echo "Documentation $build_docs" echo "uname -r $uname_release" echo "NPTL support $nptl" echo "vde support $vde" +echo "gtk support $gtk" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -1097,6 +1116,12 @@ if test "$vde" = "yes" ; then echo "#define CONFIG_VDE 1" >> $config_h echo "VDE_LIBS=-lvdeplug" >> $config_mak fi +if test "$gtk" = "yes" ; then + echo "CONFIG_GTK=yes" >> $config_mak + echo "#define CONFIG_GTK 1" >> $config_h + echo "GTK_CFLAGS=$gtk_cflags" >> $config_mak + echo "GTK_LIBS=$gtk_libs" >> $config_mak +fi for card in $audio_card_list; do def=CONFIG_`echo $card | tr '[:lower:]' '[:upper:]'` echo "$def=yes" >> $config_mak diff --git a/console.h b/console.h index e852dd1..92de255 100644 --- a/console.h +++ b/console.h @@ -140,6 +140,11 @@ void qemu_console_resize(QEMUConsole *console, int width, int height); /* sdl.c */ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); +/* gtk.c */ +void gtk_display_init(DisplayState *ds, int *argc, char ***argv); + +void gtk_display_main_loop(DisplayState *ds, int (*func)(void)); + /* cocoa.m */ void cocoa_display_init(DisplayState *ds, int full_screen); diff --git a/gdk_keysym.h b/gdk_keysym.h new file mode 100644 index 0000000..2dce2ad --- /dev/null +++ b/gdk_keysym.h @@ -0,0 +1,280 @@ +typedef struct { + const char* name; + int keysym; +} name2keysym_t; +static name2keysym_t name2keysym[]={ +/* ascii */ + { "space", GDK_space}, + { "exclam", GDK_exclam}, + { "quotedbl", GDK_quotedbl}, + { "numbersign", GDK_numbersign}, + { "dollar", GDK_dollar}, + { "percent", GDK_percent}, + { "ampersand", GDK_ampersand}, + { "apostrophe", GDK_apostrophe}, + { "parenleft", GDK_parenleft}, + { "parenright", GDK_parenright}, + { "asterisk", GDK_asterisk}, + { "plus", GDK_plus}, + { "comma", GDK_comma}, + { "minus", GDK_minus}, + { "period", GDK_period}, + { "slash", GDK_slash}, + { "0", GDK_0}, + { "1", GDK_1}, + { "2", GDK_2}, + { "3", GDK_3}, + { "4", GDK_4}, + { "5", GDK_5}, + { "6", GDK_6}, + { "7", GDK_7}, + { "8", GDK_8}, + { "9", GDK_9}, + { "colon", GDK_colon}, + { "semicolon", GDK_semicolon}, + { "less", GDK_less}, + { "equal", GDK_equal}, + { "greater", GDK_greater}, + { "question", GDK_question}, + { "at", GDK_at}, + { "A", GDK_A}, + { "B", GDK_B}, + { "C", GDK_C}, + { "D", GDK_D}, + { "E", GDK_E}, + { "F", GDK_F}, + { "G", GDK_G}, + { "H", GDK_H}, + { "I", GDK_I}, + { "J", GDK_J}, + { "K", GDK_K}, + { "L", GDK_L}, + { "M", GDK_M}, + { "N", GDK_N}, + { "O", GDK_O}, + { "P", GDK_P}, + { "Q", GDK_Q}, + { "R", GDK_R}, + { "S", GDK_S}, + { "T", GDK_T}, + { "U", GDK_U}, + { "V", GDK_V}, + { "W", GDK_W}, + { "X", GDK_X}, + { "Y", GDK_Y}, + { "Z", GDK_Z}, + { "bracketleft", GDK_bracketleft}, + { "backslash", GDK_backslash}, + { "bracketright", GDK_bracketright}, + { "asciicircum", GDK_asciicircum}, + { "underscore", GDK_underscore}, + { "grave", GDK_grave}, + { "a", GDK_a}, + { "b", GDK_b}, + { "c", GDK_c}, + { "d", GDK_d}, + { "e", GDK_e}, + { "f", GDK_f}, + { "g", GDK_g}, + { "h", GDK_h}, + { "i", GDK_i}, + { "j", GDK_j}, + { "k", GDK_k}, + { "l", GDK_l}, + { "m", GDK_m}, + { "n", GDK_n}, + { "o", GDK_o}, + { "p", GDK_p}, + { "q", GDK_q}, + { "r", GDK_r}, + { "s", GDK_s}, + { "t", GDK_t}, + { "u", GDK_u}, + { "v", GDK_v}, + { "w", GDK_w}, + { "x", GDK_x}, + { "y", GDK_y}, + { "z", GDK_z}, + { "braceleft", GDK_braceleft}, + { "bar", GDK_bar}, + { "braceright", GDK_braceright}, + { "asciitilde", GDK_asciitilde}, + +/* latin 1 extensions */ +{ "nobreakspace", GDK_nobreakspace}, +{ "exclamdown", GDK_exclamdown}, +{ "cent", GDK_cent}, +{ "sterling", GDK_sterling}, +{ "currency", GDK_currency}, +{ "yen", GDK_yen}, +{ "brokenbar", GDK_brokenbar}, +{ "section", GDK_section}, +{ "diaeresis", GDK_diaeresis}, +{ "copyright", GDK_copyright}, +{ "ordfeminine", GDK_ordfeminine}, +{ "guillemotleft", GDK_guillemotleft}, +{ "notsign", GDK_notsign}, +{ "hyphen", GDK_hyphen}, +{ "registered", GDK_registered}, +{ "macron", GDK_macron}, +{ "degree", GDK_degree}, +{ "plusminus", GDK_plusminus}, +{ "twosuperior", GDK_twosuperior}, +{ "threesuperior", GDK_threesuperior}, +{ "acute", GDK_acute}, +{ "mu", GDK_mu}, +{ "paragraph", GDK_paragraph}, +{ "periodcentered", GDK_periodcentered}, +{ "cedilla", GDK_cedilla}, +{ "onesuperior", GDK_onesuperior}, +{ "masculine", GDK_masculine}, +{ "guillemotright", GDK_guillemotright}, +{ "onequarter", GDK_onequarter}, +{ "onehalf", GDK_onehalf}, +{ "threequarters", GDK_threequarters}, +{ "questiondown", GDK_questiondown}, +{ "Agrave", GDK_Agrave}, +{ "Aacute", GDK_Aacute}, +{ "Acircumflex", GDK_Acircumflex}, +{ "Atilde", GDK_Atilde}, +{ "Adiaeresis", GDK_Adiaeresis}, +{ "Aring", GDK_Aring}, +{ "AE", GDK_AE}, +{ "Ccedilla", GDK_Ccedilla}, +{ "Egrave", GDK_Egrave}, +{ "Eacute", GDK_Eacute}, +{ "Ecircumflex", GDK_Ecircumflex}, +{ "Ediaeresis", GDK_Ediaeresis}, +{ "Igrave", GDK_Igrave}, +{ "Iacute", GDK_Iacute}, +{ "Icircumflex", GDK_Icircumflex}, +{ "Idiaeresis", GDK_Idiaeresis}, +{ "ETH", GDK_ETH}, +{ "Eth", GDK_Eth}, +{ "Ntilde", GDK_Ntilde}, +{ "Ograve", GDK_Ograve}, +{ "Oacute", GDK_Oacute}, +{ "Ocircumflex", GDK_Ocircumflex}, +{ "Otilde", GDK_Otilde}, +{ "Odiaeresis", GDK_Odiaeresis}, +{ "multiply", GDK_multiply}, +{ "Ooblique", GDK_Ooblique}, +{ "Oslash", GDK_Oslash}, +{ "Ugrave", GDK_Ugrave}, +{ "Uacute", GDK_Uacute}, +{ "Ucircumflex", GDK_Ucircumflex}, +{ "Udiaeresis", GDK_Udiaeresis}, +{ "Yacute", GDK_Yacute}, +{ "THORN", GDK_THORN}, +{ "Thorn", GDK_Thorn}, +{ "ssharp", GDK_ssharp}, +{ "agrave", GDK_agrave}, +{ "aacute", GDK_aacute}, +{ "acircumflex", GDK_acircumflex}, +{ "atilde", GDK_atilde}, +{ "adiaeresis", GDK_adiaeresis}, +{ "aring", GDK_aring}, +{ "ae", GDK_ae}, +{ "ccedilla", GDK_ccedilla}, +{ "egrave", GDK_egrave}, +{ "eacute", GDK_eacute}, +{ "ecircumflex", GDK_ecircumflex}, +{ "ediaeresis", GDK_ediaeresis}, +{ "igrave", GDK_igrave}, +{ "iacute", GDK_iacute}, +{ "icircumflex", GDK_icircumflex}, +{ "idiaeresis", GDK_idiaeresis}, +{ "eth", GDK_eth}, +{ "ntilde", GDK_ntilde}, +{ "ograve", GDK_ograve}, +{ "oacute", GDK_oacute}, +{ "ocircumflex", GDK_ocircumflex}, +{ "otilde", GDK_otilde}, +{ "odiaeresis", GDK_odiaeresis}, +{ "division", GDK_division}, +{ "oslash", GDK_oslash}, +{ "ooblique", GDK_ooblique}, +{ "ugrave", GDK_ugrave}, +{ "uacute", GDK_uacute}, +{ "ucircumflex", GDK_ucircumflex}, +{ "udiaeresis", GDK_udiaeresis}, +{ "yacute", GDK_yacute}, +{ "thorn", GDK_thorn}, +{ "ydiaeresis", GDK_ydiaeresis}, +{"EuroSign", GDK_EuroSign}, + + /* modifiers */ +{"Control_L", GDK_Control_L}, +{"Control_R", GDK_Control_R}, +{"Alt_L", GDK_Alt_L}, +{"Alt_R", GDK_Alt_R}, +{"Caps_Lock", GDK_Caps_Lock}, +{"Meta_L", GDK_Meta_L}, +{"Meta_R", GDK_Meta_R}, +{"Shift_L", GDK_Shift_L}, +{"Shift_R", GDK_Shift_R}, +{"Super_L", GDK_Super_L}, +{"Super_R", GDK_Super_R}, + + /* special keys */ +{"BackSpace", GDK_BackSpace}, +{"Tab", GDK_Tab}, +{"Return", GDK_Return}, +{"Right", GDK_Right}, +{"Left", GDK_Left}, +{"Up", GDK_Up}, +{"Down", GDK_Down}, +{"Page_Down", GDK_Page_Down}, +{"Page_Up", GDK_Page_Up}, +{"Insert", GDK_Insert}, +{"Delete", GDK_Delete}, +{"Home", GDK_Home}, +{"End", GDK_End}, +{"Scroll_Lock", GDK_Scroll_Lock}, +{"F1", GDK_F1}, +{"F2", GDK_F2}, +{"F3", GDK_F3}, +{"F4", GDK_F4}, +{"F5", GDK_F5}, +{"F6", GDK_F6}, +{"F7", GDK_F7}, +{"F8", GDK_F8}, +{"F9", GDK_F9}, +{"F10", GDK_F10}, +{"F11", GDK_F11}, +{"F12", GDK_F12}, +{"F13", GDK_F13}, +{"F14", GDK_F14}, +{"F15", GDK_F15}, +{"Sys_Req", GDK_Sys_Req}, +{"KP_0", GDK_KP_0}, +{"KP_1", GDK_KP_1}, +{"KP_2", GDK_KP_2}, +{"KP_3", GDK_KP_3}, +{"KP_4", GDK_KP_4}, +{"KP_5", GDK_KP_5}, +{"KP_6", GDK_KP_6}, +{"KP_7", GDK_KP_7}, +{"KP_8", GDK_KP_8}, +{"KP_9", GDK_KP_9}, +{"KP_Add", GDK_KP_Add}, +{"KP_Decimal", GDK_KP_Decimal}, +{"KP_Divide", GDK_KP_Divide}, +{"KP_Enter", GDK_KP_Enter}, +{"KP_Equal", GDK_KP_Equal}, +{"KP_Multiply", GDK_KP_Multiply}, +{"KP_Subtract", GDK_KP_Subtract}, +{"help", GDK_Help}, +{"Menu", GDK_Menu}, +#if 0 +{"Power", GDK_Power}, +#endif +{"Print", GDK_Print}, +{"Mode_switch", GDK_Mode_switch}, +{"Multi_Key", GDK_Multi_key}, +{"Num_Lock", GDK_Num_Lock}, +{"Pause", GDK_Pause}, +{"Escape", GDK_Escape}, + +{0,0}, +}; diff --git a/gtk.c b/gtk.c new file mode 100644 index 0000000..9d45bef --- /dev/null +++ b/gtk.c @@ -0,0 +1,356 @@ +/* + * GTK Front-end support for QEMU + * + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "console.h" +#include "sysemu.h" + +#include +#include + +typedef struct GtkDisplayState +{ + DisplayState *ds; + GtkWidget *window; + GtkWidget *widget; + GdkImage *image; + GdkGC *gc; + gint mouse_x, mouse_y; + gint mouse_buttons; +} GtkDisplayState; + +static void gtk_dpy_update(DisplayState *ds, int x, int y, int w, int h) +{ + GtkDisplayState *s = ds->opaque; + + /* Queuing the drawing area will cause an expose event to occur which is + * where we really draw the screen */ + gtk_widget_queue_draw_area(s->widget, x, y, w, h); +} + +static void gtk_dpy_resize(DisplayState *ds, int w, int h) +{ + GtkDisplayState *s = ds->opaque; + GdkVisual *visual; + + /* Free old buffer if we have to */ + if (s->image) { + g_object_unref(s->image); + s->image = NULL; + } + ds->data = NULL; + + /* Initialize new buffer */ + ds->width = w; + ds->height = h; + + /* FIXME valid all DisplayState assumptions */ + visual = gtk_widget_get_visual(s->widget); + s->image = gdk_image_new(GDK_IMAGE_FASTEST, visual, ds->width, ds->height); + ds->depth = s->image->bpp * 8; + ds->linesize = s->image->bpl; + if (s->image->byte_order == GDK_MSB_FIRST) + ds->bgr = 1; + else + ds->bgr = 0; + ds->data = s->image->mem; + + /* Set the size of the widget being used to display the VGA screen. */ + gtk_widget_set_size_request(s->widget, ds->width, ds->height); +} + +static void update_caption(GtkDisplayState *s) +{ + char buf[1024]; + const char *status = ""; + + if (!vm_running) + status = " [Stopped]"; +#if 0 + else if (gui_grab) + status = " - Press Ctrl-Alt to exit grab"; +#endif + + if (qemu_name) + snprintf(buf, sizeof(buf), "QEMU (%s)%s", qemu_name, status); + else + snprintf(buf, sizeof(buf), "QEMU%s", status); + + gtk_window_set_title(GTK_WINDOW(s->window), buf); +} + +static void gtk_dpy_refresh(DisplayState *ds) +{ + GtkDisplayState *s = ds->opaque; + static int last_vm_running = -1; + + if (last_vm_running != vm_running) { + last_vm_running = vm_running; + update_caption(s); + } + + vga_hw_update(); + + while (gtk_events_pending()) { + if (gtk_main_iteration()) { + /* GTK main loop has been exited */ + qemu_system_shutdown_request(); + vm_start(); + break; + } + } +} + +static gboolean gtk_dpy_expose(GtkWidget *widget, GdkEventExpose *expose, + gpointer opaque) +{ + GtkDisplayState *s = opaque; + gint x, y, w, h; + + /* The widget may be exposed before we are ready to go. Be defensive */ + if (widget->window == NULL || s->image == NULL) + return FALSE; + + /* Create a graphics context if we need to */ + if (s->gc == NULL) + s->gc = gdk_gc_new(widget->window); + + /* Clip expose area to DisplayState */ + x = MIN(expose->area.x, s->ds->width); + y = MIN(expose->area.y, s->ds->height); + w = MIN(expose->area.x + expose->area.width, s->ds->width); + h = MIN(expose->area.y + expose->area.height, s->ds->height); + w -= x; + h -= y; + + /* Draw screen */ + gdk_draw_image(widget->window, s->gc, s->image, x, y, x, y, w, h); + + return TRUE; +} + +#include "gdk_keysym.h" +#include "keymaps.c" + +static kbd_layout_t *kbd_layout; + +static uint8_t gtk_keyevent_to_keycode_generic(GdkEventKey *key) +{ + return keysym2scancode(kbd_layout, key->keyval); +} + +static uint8_t gtk_keyevent_to_keycode(GdkEventKey *key) +{ + int keycode; + + keycode = key->hardware_keycode; + + if (keycode < 9) { + keycode = 0; + } else if (keycode < 97) { + keycode -= 8; + } else if (keycode < 212) { + keycode = _translate_keycode(keycode - 97); + } else { + keycode = 0; + } + + return keycode; +} + +static gboolean gtk_dpy_key(GtkWidget *widget, GdkEventKey *key, + gpointer opaque) +{ + int keycode, v; + + if (key->keyval == GDK_Pause) { + v = 0; + if (key->type == GDK_KEY_PRESS) + v |= 0x80; + kbd_put_keycode(0xe1); + kbd_put_keycode(0x1d | v); + kbd_put_keycode(0x45 | v); + return TRUE; + } + + if (kbd_layout) + keycode = gtk_keyevent_to_keycode_generic(key); + else + keycode = gtk_keyevent_to_keycode(key); + + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (key->type == GDK_KEY_RELEASE) + kbd_put_keycode(keycode | 0x80); + else + kbd_put_keycode(keycode & 0x7f); + + return TRUE; +} + +static gboolean gtk_dpy_motion(GtkWidget *widget, GdkEventMotion *motion, + gpointer opaque) +{ + GtkDisplayState *s = opaque; + int dx, dy; + + if (kbd_mouse_is_absolute()) { + dx = (gint)motion->x * 0x7FFF / (s->ds->width - 1); + dy = (gint)motion->y * 0x7FFF / (s->ds->height - 1); + } else { + dx = (gint)motion->x - s->mouse_x; + dy = (gint)motion->y - s->mouse_y; + } + + s->mouse_x = (gint)motion->x; + s->mouse_y = (gint)motion->y; + + kbd_mouse_event(dx, dy, 0, s->mouse_buttons); + + return TRUE; +} + +static gboolean gtk_dpy_button(GtkWidget *widget, GdkEventButton *button, + gpointer opaque) +{ + GtkDisplayState *s = opaque; + gint mask; + + mask = 0; + if (button->button == 1) + mask |= MOUSE_EVENT_LBUTTON; + else if (button->button == 2) + mask |= MOUSE_EVENT_MBUTTON; + else if (button->button == 3) + mask |= MOUSE_EVENT_RBUTTON; + + /* make sure to ignore double and triple clicks */ + if (button->type == GDK_BUTTON_PRESS) + s->mouse_buttons |= mask; + else if (button->type == GDK_BUTTON_RELEASE) + s->mouse_buttons &= ~mask; + + if (kbd_mouse_is_absolute()) + kbd_mouse_event(s->mouse_x, s->mouse_y, 0, s->mouse_buttons); + else + kbd_mouse_event(0, 0, 0, s->mouse_buttons); + + return TRUE; +} + +static gboolean gtk_dpy_scroll(GtkWidget *widget, GdkEventScroll *scroll, + gpointer opaque) +{ + GtkDisplayState *s = opaque; + gint dz = 0; + + if (scroll->direction == GDK_SCROLL_UP) + dz = -1; + else if (scroll->direction == GDK_SCROLL_DOWN) + dz = 1; + + if (kbd_mouse_is_absolute()) + kbd_mouse_event(s->mouse_x, s->mouse_y, dz, s->mouse_buttons); + else + kbd_mouse_event(0, 0, dz, s->mouse_buttons); + + return TRUE; +} + +void gtk_display_init(DisplayState *ds, int *argc, char ***argv) +{ + GtkDisplayState *s; + + gtk_init(argc, argv); + + if (keyboard_layout) { + kbd_layout = init_keyboard_layout(keyboard_layout); + if (!kbd_layout) + exit(1); + } + + s = qemu_mallocz(sizeof(*s)); + if (s == NULL) { + fprintf(stderr, "failed to allocate GtkDisplayState\n"); + exit(1); + } + + s->ds = ds; + s->widget = gtk_drawing_area_new(); + s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_resizable(GTK_WINDOW(s->window), FALSE); + gtk_container_add(GTK_CONTAINER(s->window), s->widget); + gtk_widget_show_all(s->window); + + gtk_signal_connect(GTK_OBJECT(s->widget), "key-press-event", + GTK_SIGNAL_FUNC(gtk_dpy_key), s); + gtk_signal_connect(GTK_OBJECT(s->widget), "key-release-event", + GTK_SIGNAL_FUNC(gtk_dpy_key), s); + gtk_signal_connect(GTK_OBJECT(s->widget), "motion-notify-event", + GTK_SIGNAL_FUNC(gtk_dpy_motion), s); + gtk_signal_connect(GTK_OBJECT(s->widget), "button-press-event", + GTK_SIGNAL_FUNC(gtk_dpy_button), s); + gtk_signal_connect(GTK_OBJECT(s->widget), "button-release-event", + GTK_SIGNAL_FUNC(gtk_dpy_button), s); + gtk_signal_connect(GTK_OBJECT(s->widget), "scroll-event", + GTK_SIGNAL_FUNC(gtk_dpy_scroll), s); + gtk_signal_connect(GTK_OBJECT(s->widget), "expose-event", + GTK_SIGNAL_FUNC(gtk_dpy_expose), s); + gtk_signal_connect(GTK_OBJECT(s->window), "delete-event", + GTK_SIGNAL_FUNC(gtk_main_quit), NULL); + + GTK_WIDGET_SET_FLAGS(s->widget, GTK_CAN_FOCUS); + + gtk_widget_add_events(s->widget, + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK); + + gtk_widget_set_double_buffered(s->widget, FALSE); + gtk_widget_grab_focus(s->widget); + + ds->dpy_update = gtk_dpy_update; + ds->dpy_resize = gtk_dpy_resize; + ds->dpy_refresh = gtk_dpy_refresh; + ds->opaque = s; + + gtk_dpy_resize(ds, 640, 480); + update_caption(s); +} + +/* GTK has it's own main loop and while it provides a mechanism to + * asynchronously iterate the main loop, assumes that these methods are being + * called from something invoked by the main loop. To work around this, we + * register an idle callback that runs the QEMU main loop, and then we use the + * asynchronous iteration functions from the refresh callback. This makes GTK + * happy while allowing QEMU to control the main loop. */ +static gboolean run_main_loop(gpointer opaque) +{ + void (*func)(void); + + func = opaque; + func(); + return FALSE; +} + +/* @func is the QEMU main loop. We pass in as a function pointer to avoid + * exporting the function from vl.c */ +void gtk_display_main_loop(DisplayState *ds, int (*func)(void)) +{ + g_idle_add(run_main_loop, func); + gtk_main(); +} diff --git a/hw/ps2.c b/hw/ps2.c index 054b92f..daccc93 100644 --- a/hw/ps2.c +++ b/hw/ps2.c @@ -284,6 +284,8 @@ static void ps2_mouse_send_packet(PS2MouseState *s) unsigned int b; int dx1, dy1, dz1; + printf("sending packet\n"); + dx1 = s->mouse_dx; dy1 = s->mouse_dy; dz1 = s->mouse_dz; @@ -332,6 +334,8 @@ static void ps2_mouse_event(void *opaque, { PS2MouseState *s = opaque; + printf("status - %d\n", (s->mouse_status & MOUSE_STATUS_ENABLED)); + /* check if deltas are recorded when disabled */ if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) return; @@ -345,6 +349,9 @@ static void ps2_mouse_event(void *opaque, return; s->mouse_buttons = buttons_state; + printf("remote: %d\n", !(s->mouse_status & MOUSE_STATUS_REMOTE)); + printf("queue count %d\n", s->common.queue.count); + if (!(s->mouse_status & MOUSE_STATUS_REMOTE) && (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) { for(;;) { @@ -392,6 +399,7 @@ void ps2_write_mouse(void *opaque, int val) ps2_queue(&s->common, AUX_ACK); break; case AUX_SET_STREAM: + printf("stream\n"); s->mouse_status &= ~MOUSE_STATUS_REMOTE; ps2_queue(&s->common, AUX_ACK); break; @@ -400,6 +408,7 @@ void ps2_write_mouse(void *opaque, int val) ps2_queue(&s->common, AUX_ACK); break; case AUX_SET_REMOTE: + printf("remote\n"); s->mouse_status |= MOUSE_STATUS_REMOTE; ps2_queue(&s->common, AUX_ACK); break; diff --git a/vl.c b/vl.c index 03cd386..0410bff 100644 --- a/vl.c +++ b/vl.c @@ -9012,7 +9012,9 @@ int main(int argc, char **argv) } else #endif { -#if defined(CONFIG_SDL) +#if defined(CONFIG_GTK) + gtk_display_init(ds, &argc, &argv); +#elif defined(CONFIG_SDL) sdl_display_init(ds, full_screen, no_frame); #elif defined(CONFIG_COCOA) cocoa_display_init(ds, full_screen); @@ -9146,7 +9148,11 @@ int main(int argc, char **argv) close(fd); } +#if defined(CONFIG_GTK) + gtk_display_main_loop(ds, main_loop); +#else main_loop(); +#endif quit_timers(); #if !defined(_WIN32) --------------010205070000020007000701--