To run from the subdirectory, first do a make and then run:
./gtk-qemu -qemu ../i386-softmmu/qemu <normal qemu args...> Regards, Anthony Liguori
# HG changeset patch # User [EMAIL PROTECTED] # Node ID 1f7d5a9d262388eb7ca22a0869f0bdddb104a0bd # Parent 161ce66608016b77bb1889ce5bd046b26e942476 Add a sample GUI that uses the shared memory interface. This GUI is written in Python and uses a GTK widget. diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/Makefile --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/Makefile Sun Jul 16 17:47:31 2006 @@ -0,0 +1,50 @@ +# Makefile -- QEMU GTK GUI +# +# Copyright (C) 2006 Anthony Liguori <[EMAIL PROTECTED]> +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file COPYING in the main directory of this archive for more +# details. + +prefix ?= /usr + +CFLAGS = $(shell pkg-config --cflags gtk+-2.0 pygtk-2.0) +LDLIBS = $(shell pkg-config --libs gtk+-2.0 pygtk-2.0) + +CODEGENDIR = $(shell pkg-config --variable=codegendir pygtk-2.0) +DEFSDIR = $(shell pkg-config --variable=defsdir pygtk-2.0) + +PYVERSION = $(shell python -c "import sys; print sys.version[:3]") +PYLIBDIR = $(prefix)/lib/python$(PYVERSION)/site-packages + +CFLAGS += -I$(prefix)/include/python$(PYVERSION) + +CFLAGS += -Wall -g + +INSTALL_DIR = install -d +INSTALL_LIB = install -m755 +INSTALL_BIN = install -m755 + +all: qemu.so + +clean: + $(RM) *.o *~ qemu.defs gen-qemu.defs.c qemu.so + +qemu-screen.o: qemu-screen.c qemu-screen.h qemu-keys.h + +qemu.defs: qemu-screen.h + python $(CODEGENDIR)/h2def.py $< > $@ + +gen-qemu.defs.c: qemu.override qemu.defs + pygtk-codegen-2.0 --prefix qemu --register $(DEFSDIR)/gdk-types.defs \ + --register $(DEFSDIR)/gtk-types.defs \ + --override qemu.override qemu.defs > $@ + +qemu.so: gen-qemu.defs.o qemu-module.o qemu-screen.o + $(CC) $(LDLIBS) -shared $^ -o $@ + +install: qemu.so + $(INSTALL_DIR) $(DESTDIR)$(prefix)/bin + $(INSTALL_BIN) gtk-qemu $(DESTDIR)$(prefix)/bin/gtk-qemu + $(INSTALL_DIR) $(DESTDIR)$(PYLIBDIR) + $(INSTALL_LIB) qemu.so $(DESTDIR)$(PYLIBDIR)/qemu.so diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/README --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/README Sun Jul 16 17:47:31 2006 @@ -0,0 +1,23 @@ +QEMU GTK GUI + +This is a simple GUI that makes use of QEMU's new shared memory interface. +It uses a XShmImage so that there is virtually no performance overhead to +having the screen appear in an external application. + +Interacting with this GUI is very similar to interacting with the SDL GUI with +a few important exceptions. + + - There is currently no full screen mode so ctrl-alt-f and -fs do nothing. + + - Instead of switching virtual consoles with ctrl-alt-N, virtual consoles will + appear as tabs within a notebook. + + - There is an additional -qemu option which can be used to specify the + location of the QEMU executable that you want this GUI to use (normally + `qemu' is executed using whatever the current path is). + +The hope is that this will provide an example on constructing GUIs that use +QEMU and perhaps will grow in the future to a much more user friendly interface +to QEMU. + + aliguori diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/gtk-qemu --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/gtk-qemu Sun Jul 16 17:47:31 2006 @@ -0,0 +1,228 @@ +#!/usr/bin/env python +# +# gtk-qemu -- QEMU GTK GUI +# +# Copyright (C) 2006 Anthony Liguori <[EMAIL PROTECTED]> +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file COPYING in the main directory of this archive for more +# details. + +import sys, os, signal +from subprocess import Popen, PIPE +from select import select + +def alloc_pty(): + (master, slave) = os.openpty() + return (master, os.ttyname(slave)) + +def parse_args(args, withargs): + i = 0 + while i < len(args): + if args[i] in withargs: + a = (args[i], args[i + 1]) + i += 1 + else: + a = (args[i], None) + i += 1 + yield a + +def wait_for_error(fd, pid): + while True: + r, w, e = select([fd], [], [], .5) + if len(r) == 0: + f, s = os.waitpid(pid, os.WNOHANG) + if f != 0 or s != 0: + return True + else: + return False + +class MouseHandler(object): + def __init__(self, screen, window): + self.screen = screen + self.window = window + self.in_grab = False + self.relative = screen.relative_pointer() + + def do_grab(self): + self.screen.enable_pointer(True) + self.window.set_title('QEMU - Press Ctrl-Alt to exit grab') + gtk.gdk.pointer_grab(self.screen.window, + event_mask=gtk.gdk.BUTTON_PRESS_MASK | + gtk.gdk.BUTTON_RELEASE_MASK | + gtk.gdk.BUTTON_MOTION_MASK | + gtk.gdk.POINTER_MOTION_MASK) + gtk.gdk.keyboard_grab(self.screen.window) + self.in_grab = True + + def do_ungrab(self): + if self.relative: + self.screen.enable_pointer(False) + self.window.set_title('QEMU') + gtk.gdk.pointer_ungrab() + gtk.gdk.keyboard_ungrab() + self.in_grab = False + + def toggle_grab(self): + if self.in_grab: + self.do_ungrab() + else: + self.do_grab() + + def force_grab(self): + self.check_grab() + + if not self.in_grab and self.screen.relative_pointer(): + self.do_grab() + + def check_grab(self, x=None, y=None): + if self.relative != self.screen.relative_pointer(): + if self.relative: + self.relative = False + if self.in_grab: + self.do_ungrab() + else: + self.screen.enable_pointer(True) + else: + self.relative = True + if not self.in_grab: + self.screen.enable_pointer(False) + if self.in_grab and (x and y): + scrn = gtk.gdk.screen_get_default() + need_warp = False + if x < 20: + x += 20 + need_warp = True + if y < 20: + y += 20 + need_warp = True + if x > scrn.get_width(): + x -= 20 + need_warp = True + if y > scrn.get_height(): + y -= 20 + need_warp = True + + if need_warp: + self.screen.warp_pointer(x, y) + +def enable_grab(widget, event, handler): + if event.button == 1: + handler.force_grab() + return False + +def toggle_grab(widget, event, handler): + if ((event.keyval == gtk.gdk.keyval_from_name('Control_L') and + event.state == gtk.gdk.MOD1_MASK) or + (event.keyval == gtk.gdk.keyval_from_name('Alt_L') and + event.state == gtk.gdk.CONTROL_MASK)): + handler.toggle_grab() + return False + +def check_grab(widget, event, handler): + handler.check_grab(int(event.x_root), int(event.y_root)) + return False + +wargs = ['-M', '-fda', '-fdb', '-hda', '-hdb', '-hdc', '-hdd', '-cdrom', + '-boot', '-m', '-smp', '-k', '-soundhw', '-usbdevice', '-net', + '-tftp', '-smb', '-redir', '-kernel', '-append', '-initrd', + '-monitor', '-serial', '-parallel', '-pidfile', '-p','-hdachs', + '-L', '-loadvm', '-vnc', '-qemu'] + +channels = [] + +shmem_fd, name = alloc_pty() +args = ['qemu', '-shmem', 'pipe:%s' % name] +channels.append(('-shmem', shmem_fd)) + +has_dev = {'-serial' : 0, '-monitor': 0, '-parallel': 0} + +# search arguments for virtual consoles +for key, value in parse_args(sys.argv[1:], wargs): + if key in ['-serial', '-monitor', '-parallel']: + if value == 'vc': + fd, name = alloc_pty() + value = 'pipe:%s' % name + + title = key + if key != '-monitor': + title += str(has_dev[key]) + + channels.append((title, fd)) + has_dev[key] += 1 + elif key == '-qemu': + args[0] = value + continue + + args.append(key) + if value != None: + args.append(value) + +# add default virtual consoles +for x in ['-parallel', '-serial', '-monitor']: + if not has_dev[x] > 0: + fd, name = alloc_pty() + args += [x, 'pipe:%s' % name] + if x != '-monitor': + x += '0' + channels = [(x, fd)] + channels + +import qemu, gtk, vte, termios, tty, gtk.gdk, gobject + +win = gtk.Window() +win.set_resizable(False) +win.set_title('QEMU') +win.connect('delete-event', gtk.main_quit) + +screen = qemu.Screen() + +handler = MouseHandler(screen, win) + +screen.connect('button-press-event', enable_grab, handler) +screen.connect('key-press-event', toggle_grab, handler) +screen.connect('motion-notify-event', check_grab, handler) + +# create notebook and add vga tab +nb = gtk.Notebook() +nb.insert_page(screen, gtk.Label('vga')) + +# create notebook tabs foreach virtual console +for kind, fd in channels: + if kind == '-shmem': + screen.attach(fd) + else: + term = vte.Terminal() + tty.setraw(fd, termios.TCSANOW) + term.set_pty(fd) + term.set_font_from_string('monospace') + term.set_scrollback_lines(1000) + w = gtk.ScrolledWindow() + w.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + w.add_with_viewport(term) + nb.insert_page(w, gtk.Label(kind[1:])) + +# when qemu exits, close the gui +def on_qemu_exit(pid, status, q): + q.pid = -1 + gtk.main_quit() + +q = Popen(args) +gobject.child_watch_add(q.pid, on_qemu_exit, q) + +# if there is only the VGA page, don't bother showing tabs +if nb.get_n_pages() == 1: + nb.set_show_tabs(False) + nb.set_show_border(False) +win.add(nb) + +if not wait_for_error(shmem_fd, q.pid): + win.show_all() + screen.grab_focus() + + gtk.main() + + screen.detach() + + # kill qemu if it's still around + if q.pid != -1: + os.kill(q.pid, signal.SIGTERM) diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/qemu-keys.h --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/qemu-keys.h Sun Jul 16 17:47:31 2006 @@ -0,0 +1,252 @@ +/* + * qemu-keys.h -- QEMU GTK Widget + * + * Copyright (C) 2006 Anthony Liguori <[EMAIL PROTECTED]> + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.lib in the main directory of this + * archive for more details. + */ + +#ifndef _QEMU_KEYS_H_ +#define _QEMU_KEYS_H_ + +#include <gdk/gdkkeysyms.h> + +static const char *qemu_keymap[0x10000] = { + [GDK_A] = "a", + [GDK_B] = "b", + [GDK_C] = "c", + [GDK_D] = "d", + [GDK_E] = "e", + [GDK_F] = "f", + [GDK_G] = "g", + [GDK_H] = "h", + [GDK_I] = "i", + [GDK_J] = "j", + [GDK_K] = "k", + [GDK_L] = "l", + [GDK_M] = "m", + [GDK_N] = "n", + [GDK_O] = "o", + [GDK_P] = "p", + [GDK_Q] = "q", + [GDK_R] = "r", + [GDK_S] = "s", + [GDK_T] = "t", + [GDK_U] = "u", + [GDK_V] = "v", + [GDK_W] = "w", + [GDK_X] = "x", + [GDK_Y] = "y", + [GDK_Z] = "z", + + [GDK_bracketleft] = "bracketleft", + [GDK_backslash] = "backslash", + [GDK_bracketright] = "bracketright", + [GDK_asciicircum] = "asciicircum", + [GDK_underscore] = "underscore", + [GDK_grave] = "grave", + + [GDK_a] = "a", + [GDK_b] = "b", + [GDK_c] = "c", + [GDK_d] = "d", + [GDK_e] = "e", + [GDK_f] = "f", + [GDK_g] = "g", + [GDK_h] = "h", + [GDK_i] = "i", + [GDK_j] = "j", + [GDK_k] = "k", + [GDK_l] = "l", + [GDK_m] = "m", + [GDK_n] = "n", + [GDK_o] = "o", + [GDK_p] = "p", + [GDK_q] = "q", + [GDK_r] = "r", + [GDK_s] = "s", + [GDK_t] = "t", + [GDK_u] = "u", + [GDK_v] = "v", + [GDK_w] = "w", + [GDK_x] = "x", + [GDK_y] = "y", + [GDK_z] = "z", + + [GDK_BackSpace] = "BackSpace", + [GDK_Tab] = "Tab", + [GDK_Linefeed] = "Linefeed", + [GDK_Clear] = "Clear", + [GDK_Return] = "Return", + [GDK_Pause] = "Pause", + [GDK_Scroll_Lock] = "Scroll_Lock", + [GDK_Sys_Req] = "Sys_Req", + [GDK_Escape] = "Escape", + [GDK_Delete] = "Delete", + + [GDK_space] = "space", + [GDK_exclam] = "exclam", + [GDK_quotedbl] = "quotedbl", + [GDK_numbersign] = "numbersign", + [GDK_dollar] = "dollar", + [GDK_percent] = "percent", + [GDK_ampersand] = "ampersand", + [GDK_apostrophe] = "apostrophe", + [GDK_parenleft] = "parenleft", + [GDK_parenright] = "parenright", + [GDK_asterisk] = "asterisk", + [GDK_plus] = "plus", + [GDK_comma] = "comma", + [GDK_minus] = "minus", + [GDK_period] = "period", + [GDK_slash] = "slash", + [GDK_0] = "0", + [GDK_1] = "1", + [GDK_2] = "2", + [GDK_3] = "3", + [GDK_4] = "4", + [GDK_5] = "5", + [GDK_6] = "6", + [GDK_7] = "7", + [GDK_8] = "8", + [GDK_9] = "9", + [GDK_colon] = "colon", + [GDK_semicolon] = "semicolon", + [GDK_less] = "less", + [GDK_equal] = "equal", + [GDK_greater] = "greater", + [GDK_question] = "question", + [GDK_at] = "at", + + [GDK_Control_L] = "Control_L", + [GDK_Control_R] = "Control_R", + [GDK_Alt_L] = "Alt_L", + [GDK_Alt_R] = "Alt_R", + [GDK_Shift_L] = "Shift_L", + [GDK_Shift_R] = "Shift_R", + + [GDK_Caps_Lock] = "Caps_Lock", + [GDK_Shift_Lock] = "Shift_Lock", + [GDK_Meta_L] = "Meta_L", + [GDK_Meta_R] = "Meta_R", + [GDK_Super_L] = "Super_L", + [GDK_Super_R] = "Super_R", + [GDK_Hyper_L] = "Hyper_L", + [GDK_Hyper_R] = "Hyper_R", + + [GDK_Home] = "Home", + [GDK_Left] = "Left", + [GDK_Up] = "Up", + [GDK_Right] = "Right", + [GDK_Down] = "Down", + [GDK_Prior] = "Prior", + [GDK_Page_Up] = "Page_Up", + [GDK_Next] = "Next", + [GDK_Page_Down] = "Page_Down", + [GDK_End] = "End", + [GDK_Begin] = "Begin", + [GDK_Select] = "Select", + [GDK_Print] = "Print", + [GDK_Execute] = "Execute", + [GDK_Insert] = "Insert", + [GDK_Undo] = "Undo", + [GDK_Redo] = "Redo", + [GDK_Menu] = "Menu", + [GDK_Find] = "Find", + [GDK_Cancel] = "Cancel", + [GDK_Help] = "Help", + [GDK_Break] = "Break", + [GDK_Mode_switch] = "Mode_switch", + [GDK_script_switch] = "script_switch", + [GDK_Num_Lock] = "Num_Lock", + [GDK_KP_Space] = "KP_Space", + [GDK_KP_Tab] = "KP_Tab", + [GDK_KP_Enter] = "KP_Enter", + [GDK_KP_F1] = "KP_F1", + [GDK_KP_F2] = "KP_F2", + [GDK_KP_F3] = "KP_F3", + [GDK_KP_F4] = "KP_F4", + [GDK_KP_Home] = "KP_Home", + [GDK_KP_Left] = "KP_Left", + [GDK_KP_Up] = "KP_Up", + [GDK_KP_Right] = "KP_Right", + [GDK_KP_Down] = "KP_Down", + [GDK_KP_Prior] = "KP_Prior", + [GDK_KP_Page_Up] = "KP_Page_Up", + [GDK_KP_Next] = "KP_Next", + [GDK_KP_Page_Down] = "KP_Page_Down", + [GDK_KP_End] = "KP_End", + [GDK_KP_Begin] = "KP_Begin", + [GDK_KP_Insert] = "KP_Insert", + [GDK_KP_Delete] = "KP_Delete", + [GDK_KP_Equal] = "KP_Equal", + [GDK_KP_Multiply] = "KP_Multiply", + [GDK_KP_Add] = "KP_Add", + [GDK_KP_Separator] = "KP_Separator", + [GDK_KP_Subtract] = "KP_Subtract", + [GDK_KP_Decimal] = "KP_Decimal", + [GDK_KP_Divide] = "KP_Divide", + [GDK_KP_0] = "KP_0", + [GDK_KP_1] = "KP_1", + [GDK_KP_2] = "KP_2", + [GDK_KP_3] = "KP_3", + [GDK_KP_4] = "KP_4", + [GDK_KP_5] = "KP_5", + [GDK_KP_6] = "KP_6", + [GDK_KP_7] = "KP_7", + [GDK_KP_8] = "KP_8", + [GDK_KP_9] = "KP_9", + [GDK_F1] = "F1", + [GDK_F2] = "F2", + [GDK_F3] = "F3", + [GDK_F4] = "F4", + [GDK_F5] = "F5", + [GDK_F6] = "F6", + [GDK_F7] = "F7", + [GDK_F8] = "F8", + [GDK_F9] = "F9", + [GDK_F10] = "F10", + [GDK_F11] = "F11", + [GDK_F12] = "F12", + + [GDK_braceleft] = "braceleft", + [GDK_bar] = "bar", + [GDK_braceright] = "braceright", + [GDK_asciitilde] = "asciitilde", + [GDK_nobreakspace] = "nobreakspace", + [GDK_exclamdown] = "exclamdown", + [GDK_cent] = "cent", + [GDK_sterling] = "sterling", + [GDK_currency] = "currency", + [GDK_yen] = "yen", + [GDK_brokenbar] = "brokenbar", + [GDK_section] = "section", + [GDK_diaeresis] = "diaeresis", + [GDK_copyright] = "copyright", + [GDK_ordfeminine] = "ordfeminine", + [GDK_guillemotleft] = "guillemotleft", + [GDK_notsign] = "notsign", + [GDK_hyphen] = "hyphen", + [GDK_registered] = "registered", + [GDK_macron] = "macron", + [GDK_degree] = "degree", + [GDK_plusminus] = "plusminus", + [GDK_twosuperior] = "twosuperior", + [GDK_threesuperior] = "threesuperior", + [GDK_acute] = "acute", + [GDK_mu] = "mu", + [GDK_paragraph] = "paragraph", + [GDK_periodcentered] = "periodcentered", + [GDK_cedilla] = "cedilla", + [GDK_onesuperior] = "onesuperior", + [GDK_masculine] = "masculine", + [GDK_guillemotright] = "guillemotright", + [GDK_onequarter] = "onequarter", + [GDK_onehalf] = "onehalf", + [GDK_threequarters] = "threequarters", + [GDK_questiondown] = "questiondown", +}; + +#endif diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/qemu-module.c --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/qemu-module.c Sun Jul 16 17:47:31 2006 @@ -0,0 +1,36 @@ +/* + * qemu-module.c -- QEMU GTK Widget Python Bindings + * + * Copyright (C) 2006 Anthony Liguori <[EMAIL PROTECTED]> + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.lib in the main directory of this + * archive for more details. + */ + +#include <pygobject.h> + +void qemu_register_classes (PyObject *d); +extern PyMethodDef qemu_functions[]; + +DL_EXPORT(void) +initqemu(void) +{ + PyObject *m, *d; + + init_pygobject (); + + m = Py_InitModule ("qemu", qemu_functions); + if (PyErr_Occurred()) + Py_FatalError("can't init module"); + + d = PyModule_GetDict (m); + if (PyErr_Occurred()) + Py_FatalError("can't get dict"); + + qemu_register_classes (d); + + if (PyErr_Occurred ()) { + Py_FatalError ("can't initialise module qemu"); + } +} diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/qemu-screen.c --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/qemu-screen.c Sun Jul 16 17:47:31 2006 @@ -0,0 +1,592 @@ +/* + * qemu-screen.c -- QEMU GTK Widget + * + * Copyright (C) 2006 Anthony Liguori <[EMAIL PROTECTED]> + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.lib in the main directory of this + * archive for more details. + */ + +#include "qemu-screen.h" + +#include <gtk/gtk.h> + +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <sys/ipc.h> +#include <sys/shm.h> +#include <stdarg.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/XShm.h> + +#include <gdk/gdkx.h> + +#include "qemu-keys.h" + +#define QEMU_SCREEN_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), QEMU_TYPE_SCREEN, QemuScreenPriv)) + +struct _QemuScreenPriv +{ + int fd; + GIOChannel *channel; + int id; + char line[1024]; + int offset; + int shmid; + XImage *ximage; + XShmSegmentInfo shm_info; + GdkGC *gc; + gboolean report_pointer; + /* TODO use signal */ + gboolean relative_pointer; + GdkCursor *null_cursor; + int x_offset, y_offset; +}; + +/* TODO deref gc on exit; cursor too */ + +static gboolean startswith(const char *lhs, const char *rhs) +{ + if (strlen(lhs) < strlen(rhs)) + return FALSE; + return (strncmp(lhs, rhs, strlen(rhs)) == 0); +} + +static void qemu_screen_send_cmd(QemuScreen *obj, const char *fmt, ...) +{ + QemuScreenPriv *priv = obj->priv; + char cmd[1024]; + va_list ap; + int len; + + va_start(ap, fmt); + len = vsnprintf(cmd, sizeof(cmd), fmt, ap); + va_end(ap); + + if (priv->fd != -1 && write(priv->fd, cmd, len) != len) { + qemu_screen_detach(obj); + g_warning("partial write?\n"); + } +} + +static void qemu_screen_class_init(QemuScreenClass *klass) +{ + g_type_class_add_private(klass, sizeof(QemuScreenPriv)); +} + +static gboolean qemu_screen_expose(GtkWidget *widget, GdkEventExpose *expose, + gpointer data) +{ + QemuScreen *obj = QEMU_SCREEN(widget); + QemuScreenPriv *priv = obj->priv; + + if (priv->ximage) { + int x, y, w, h; + + x = MIN(expose->area.x, priv->ximage->width); + y = MIN(expose->area.y, priv->ximage->height); + w = MIN(expose->area.x + expose->area.width, priv->ximage->width); + h = MIN(expose->area.y + expose->area.height, priv->ximage->height); + + w -= x; + h -= y; + + XShmPutImage(GDK_GC_XDISPLAY(priv->gc), + GDK_DRAWABLE_XID(widget->window), + GDK_GC_XGC(priv->gc), + priv->ximage, + x, y, x, y, w, h, False); + } + + return TRUE; +} + +static gboolean qemu_screen_motion(GtkWidget *widget, GdkEventMotion *motion, + gpointer data) +{ + QemuScreen *obj = QEMU_SCREEN(widget); + QemuScreenPriv *priv = obj->priv; + int buttons = 0; + + if (motion->state & 0x100) + buttons |= 1; + if (motion->state & 0x400) + buttons |= 2; + if (motion->state & 0x200) + buttons |= 4; + + if (!priv->relative_pointer || priv->report_pointer) { + qemu_screen_send_cmd(obj, "MOUSE %d, %d, %d, %d\n", + (int)motion->x + priv->x_offset, + (int)motion->y + priv->y_offset, + 0, buttons); + } + + return FALSE; +} + +static gboolean qemu_screen_button(GtkWidget *widget, GdkEventButton *button, + gpointer data) +{ + QemuScreen *obj = QEMU_SCREEN(widget); + QemuScreenPriv *priv = obj->priv; + int buttons = 0; + int b; + + if (button->state & 0x100) + buttons |= 1; + if (button->state & 0x400) + buttons |= 2; + if (button->state & 0x200) + buttons |= 4; + + if (button->button == 1) + b = 0; + else if (button->button == 3) + b = 1; + else if (button->button == 2) + b = 2; + else + return FALSE; + + if (button->type == GDK_BUTTON_PRESS) + buttons |= (1 << b); + else if (button->type == GDK_BUTTON_RELEASE) + buttons &= ~(1 << b); + + if (!priv->relative_pointer || priv->report_pointer) { + qemu_screen_send_cmd(obj, "MOUSE %d, %d, %d, %d\n", + (int)button->x + priv->x_offset, + (int)button->y + priv->y_offset, + 0, buttons); + } + + return FALSE; +} + +static gboolean qemu_screen_scroll(GtkWidget *widget, GdkEventScroll *scroll, + gpointer data) +{ + QemuScreen *obj = QEMU_SCREEN(widget); + QemuScreenPriv *priv = obj->priv; + int buttons = 0; + int dz = 0; + + if (scroll->state & 0x100) + buttons |= 1; + if (scroll->state & 0x400) + buttons |= 2; + if (scroll->state & 0x200) + buttons |= 4; + + if (scroll->direction == GDK_SCROLL_UP) + dz = -1; + else if (scroll->direction == GDK_SCROLL_DOWN) + dz = 1; + + if (!priv->relative_pointer || priv->report_pointer) { + qemu_screen_send_cmd(obj, "MOUSE %d, %d, %d, %d\n", + (int)scroll->x + priv->x_offset, + (int)scroll->y + priv->y_offset, + dz, buttons); + } + + return TRUE; +} + +static gboolean qemu_screen_key(GtkWidget *widget, GdkEventKey *key, + gpointer data) +{ + QemuScreen *obj = QEMU_SCREEN(widget); + const char *name; + int down; + + if (key->type == GDK_KEY_PRESS) + down = 1; + else + down = 0; + + if (key->keyval >= 0x10000) + return FALSE; + + name = qemu_keymap[key->keyval]; + + if (name) + qemu_screen_send_cmd(obj, "KEY-%s %s\n", down ? "DOWN" : "UP", name); + + return FALSE; +} + +static GdkCursor *create_null_cursor(void) +{ + GdkBitmap *image; + gchar data[4] = {0}; + GdkColor fg = { 0 }; + GdkCursor *cursor; + + image = gdk_bitmap_create_from_data(NULL, data, 1, 1); + + cursor = gdk_cursor_new_from_pixmap(GDK_PIXMAP(image), + GDK_PIXMAP(image), + &fg, &fg, 0, 0); + gdk_bitmap_unref(image); + + return cursor; +} + +static void qemu_screen_init(GTypeInstance *instance, gpointer klass) +{ + QemuScreen *obj = QEMU_SCREEN(instance); + obj->priv = QEMU_SCREEN_GET_PRIVATE(obj); + + obj->priv->fd = -1; + obj->priv->channel = NULL; + obj->priv->id = -1; + obj->priv->offset = 0; + obj->priv->ximage = NULL; + obj->priv->shm_info.shmaddr = (void *)-1; + obj->priv->shm_info.shmid = -1; + obj->priv->gc = NULL; + obj->priv->report_pointer = FALSE; + obj->priv->relative_pointer = TRUE; + obj->priv->null_cursor = create_null_cursor(); + obj->priv->x_offset = 0; + obj->priv->y_offset = 0; + + gtk_signal_connect(GTK_OBJECT(obj), + "expose-event", + GTK_SIGNAL_FUNC(qemu_screen_expose), + NULL); + + gtk_signal_connect(GTK_OBJECT(obj), + "motion-notify-event", + GTK_SIGNAL_FUNC(qemu_screen_motion), + NULL); + + gtk_signal_connect(GTK_OBJECT(obj), + "button-press-event", + GTK_SIGNAL_FUNC(qemu_screen_button), + NULL); + + gtk_signal_connect(GTK_OBJECT(obj), + "button-release-event", + GTK_SIGNAL_FUNC(qemu_screen_button), + NULL); + + gtk_signal_connect(GTK_OBJECT(obj), + "scroll-event", + GTK_SIGNAL_FUNC(qemu_screen_scroll), + NULL); + + GTK_WIDGET_SET_FLAGS(GTK_OBJECT(obj), GTK_CAN_FOCUS); + + gtk_signal_connect(GTK_OBJECT(obj), + "key-press-event", + GTK_SIGNAL_FUNC(qemu_screen_key), + NULL); + + gtk_signal_connect(GTK_OBJECT(obj), + "key-release-event", + GTK_SIGNAL_FUNC(qemu_screen_key), + NULL); + + gtk_widget_set_double_buffered(GTK_WIDGET(obj), FALSE); + + gtk_widget_add_events(GTK_WIDGET(obj), + GDK_BUTTON_MOTION_MASK | + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_SCROLL_MASK); +} + +GType qemu_screen_get_type(void) +{ + static GType type; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(QemuScreenClass), + NULL, + NULL, + (GClassInitFunc)qemu_screen_class_init, + NULL, + NULL, + sizeof(QemuScreen), + 0, + qemu_screen_init, + }; + + type = g_type_register_static(GTK_TYPE_DRAWING_AREA, + "QemuScreen", + &info, + 0); + } + + return type; +} + +static void qemu_screen_resize(QemuScreen *obj, int width, int height) +{ + QemuScreenPriv *priv = obj->priv; + size_t size; + GdkDrawable *drawable; + + drawable = GTK_WIDGET(obj)->window; + + if (priv->gc == NULL) + priv->gc = gdk_gc_new(drawable); + + if (priv->ximage) { + XDestroyImage(priv->ximage); + shmdt(priv->shm_info.shmaddr); + shmctl(priv->shm_info.shmid, IPC_RMID, NULL); + + priv->ximage = NULL; + priv->shm_info.shmaddr = (void *)-1; + priv->shm_info.shmid = -1; + } + + priv->ximage = XShmCreateImage(GDK_GC_XDISPLAY(priv->gc), + GDK_VISUAL_XVISUAL(gdk_visual_get_best()), + gdk_drawable_get_depth(drawable), + ZPixmap, NULL, &priv->shm_info, + width, height); + if (priv->ximage == NULL) { + qemu_screen_detach(obj); + return; + } + + size = priv->ximage->bytes_per_line * priv->ximage->height; + priv->shm_info.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600); + if (priv->shm_info.shmid == -1) { + qemu_screen_detach(obj); + return; + } + + priv->shm_info.readOnly = False; + priv->shm_info.shmaddr = shmat(priv->shm_info.shmid, NULL, 0); + if (priv->shm_info.shmaddr == (void *)-1) { + qemu_screen_detach(obj); + return; + } + + priv->ximage->data = priv->shm_info.shmaddr; + + gdk_error_trap_push(); + XShmAttach(GDK_GC_XDISPLAY(priv->gc), &priv->shm_info); + XSync(GDK_GC_XDISPLAY(priv->gc), False); + if (gdk_error_trap_pop()) { + qemu_screen_detach(obj); + return; + } + + qemu_screen_send_cmd(obj, "SHMID %d, %d, %d, %d, %d, %d\n", + priv->shm_info.shmid, size, + width, height, + priv->ximage->bits_per_pixel, + priv->ximage->bytes_per_line); + + gtk_widget_set_size_request(GTK_WIDGET(obj), width, height); +} + +static void qemu_screen_update(QemuScreen *obj, int x, int y, int w, int h) +{ + gtk_widget_queue_draw_area(GTK_WIDGET(obj), x, y, w, h); +} + +static void qemu_screen_attached(QemuScreen *obj, int shmid) +{ + QemuScreenPriv *priv = obj->priv; + + if (priv->shm_info.shmid == shmid) { + shmctl(shmid, IPC_RMID, NULL); + priv->shm_info.shmid = -1; + } +} + +static void qemu_screen_handle_cmd(QemuScreen *obj, const char *line) +{ + QemuScreenPriv *priv = obj->priv; + int width, height, x, y, shmid; + + if (sscanf(line, "UPDATE %d, %d, %d, %d\n", + &x, &y, &width, &height) == 4) { + qemu_screen_update(obj, x, y, width, height); + } else if (sscanf(line, "RESIZE %d, %d\n", &width, &height) == 2) { + qemu_screen_resize(obj, width, height); + } else if (sscanf(line, "ATTACHED %d\n", &shmid) == 1) { + qemu_screen_attached(obj, shmid); + } else if (startswith(line, "MOUSE-ABSOLUTE")) { + priv->relative_pointer = FALSE; + priv->x_offset = 0; + priv->y_offset = 0; + } else if (startswith(line, "MOUSE-RELATIVE")) { + priv->relative_pointer = TRUE; + } else if (startswith(line, "ERROR")) { + g_warning(line + 6); + } +} + +static gboolean qemu_screen_read(GIOChannel *src, GIOCondition cond, + gpointer data) +{ + QemuScreen *obj = data; + QemuScreenPriv *priv = obj->priv; + ssize_t len; + char *nl; + + len = read(priv->fd, + priv->line + priv->offset, + sizeof(priv->line) - priv->offset - 1); + if (len == 0) { + qemu_screen_detach(obj); + return FALSE; + } + + if (len == -1) { + if (errno == EINTR || errno == EAGAIN) + return TRUE; + qemu_screen_detach(obj); + return FALSE; + } + + priv->offset += len; + priv->line[priv->offset] = 0; + + while ((nl = strchr(priv->line, '\n'))) { + *nl = 0; + qemu_screen_handle_cmd(obj, priv->line); + len = priv->offset - (nl - priv->line); + memmove(priv->line, nl + 1, len - 1); + priv->offset = len - 1; + } + + if (priv->offset == sizeof(priv->line) - 1) { + g_warning("QemuScreen: buffer full"); + qemu_screen_detach(obj); + return FALSE; + } + + return TRUE; +} + +GtkWidget *qemu_screen_new(void) +{ + return GTK_WIDGET(g_object_new(QEMU_TYPE_SCREEN, NULL)); +} + +void qemu_screen_detach(QemuScreen *obj) +{ + QemuScreenPriv *priv = obj->priv; + + if (priv->ximage) { + XDestroyImage(priv->ximage); + priv->ximage = NULL; + } + + if (priv->shm_info.shmaddr != (void *)-1) { + shmdt(priv->shm_info.shmaddr); + priv->shm_info.shmaddr = (void *)-1; + } + + if (priv->shm_info.shmid != -1) { + shmctl(priv->shm_info.shmid, IPC_RMID, NULL); + priv->shm_info.shmid = -1; + } + + if (priv->id != -1) { + g_source_remove(priv->id); + priv->id = -1; + } + + if (priv->channel) { + g_io_channel_unref(priv->channel); + priv->channel = NULL; + } + + if (priv->fd != -1) { + close(priv->fd); + priv->fd = -1; + } + + obj->priv->report_pointer = FALSE; + obj->priv->relative_pointer = TRUE; +} + +gboolean qemu_screen_attach(QemuScreen *obj, int fd) +{ + QemuScreenPriv *priv = obj->priv; + + priv->fd = fd; + if (priv->fd == -1) + return FALSE; + + priv->channel = g_io_channel_unix_new(priv->fd); + priv->id = g_io_add_watch(priv->channel, G_IO_IN, qemu_screen_read, obj); + + return TRUE; +} + +gboolean qemu_screen_enable_pointer(QemuScreen *obj, gboolean enable) +{ + QemuScreenPriv *priv = obj->priv; + gboolean ret; + GdkCursor *cursor = NULL; + + if (enable != priv->report_pointer) { + priv->x_offset = 0; + priv->y_offset = 0; + } + + ret = priv->report_pointer; + priv->report_pointer = enable; + + if (enable) + cursor = priv->null_cursor; + + gdk_window_set_cursor(GTK_WIDGET(obj)->window, cursor); + + return ret; +} + +gboolean qemu_screen_pointer_enabled(QemuScreen *obj) +{ + QemuScreenPriv *priv = obj->priv; + + return priv->report_pointer; +} + +gboolean qemu_screen_relative_pointer(QemuScreen *obj) +{ + QemuScreenPriv *priv = obj->priv; + + return priv->relative_pointer; +} + +void qemu_screen_warp_pointer(QemuScreen *obj, int x, int y) +{ + QemuScreenPriv *priv = obj->priv; + GdkDisplay *dpy = gdk_display_get_default(); + GdkScreen *screen; + int dx, dy; + GdkModifierType mask; + + gdk_display_get_pointer(dpy, &screen, &dx, &dy, &mask); + + if (priv->relative_pointer) { + priv->x_offset += (dx - x); + priv->y_offset += (dy - y); + gdk_display_warp_pointer(dpy, screen, x, y); + } +} diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/qemu-screen.h --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/qemu-screen.h Sun Jul 16 17:47:31 2006 @@ -0,0 +1,65 @@ +/* + * qemu-screen.h -- QEMU GTK Widget + * + * Copyright (C) 2006 Anthony Liguori <[EMAIL PROTECTED]> + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.lib in the main directory of this + * archive for more details. + */ + +#ifndef _QEMU_SCREEN_H_ +#define _QEMU_SCREEN_H_ + +typedef struct _QemuScreen QemuScreen; +typedef struct _QemuScreenClass QemuScreenClass; +typedef struct _QemuScreenPriv QemuScreenPriv; + +#include <gtk/gtkdrawingarea.h> + +#define QEMU_TYPE_SCREEN (qemu_screen_get_type()) + +#define QEMU_SCREEN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), QEMU_TYPE_SCREEN, QemuScreen)) + +#define QEMU_SCREEN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), QEMU_TYPE_SCREEN, QemuScreenClass)) + +#define QEMU_IS_SCREEN(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), QEMU_TYPE_SCREEN)) + +#define QEMU_IS_SCREEN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), QEMU_TYPE_SCREEN)) + +#define QEMU_SCREEN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), QEMU_TYPE_SCREEN, QemuScreenClass)) + +struct _QemuScreen +{ + GtkDrawingArea parent; + + QemuScreenPriv *priv; +}; + +struct _QemuScreenClass +{ + GtkDrawingAreaClass parent; +}; + +G_BEGIN_DECLS + +GType qemu_screen_get_type(void); +GtkWidget * qemu_screen_new(void); + +gboolean qemu_screen_attach(QemuScreen *obj, int fd); +void qemu_screen_detach(QemuScreen *obj); + +gboolean qemu_screen_enable_pointer(QemuScreen *obj, gboolean enable); +gboolean qemu_screen_pointer_enabled(QemuScreen *obj); +gboolean qemu_screen_relative_pointer(QemuScreen *obj); + +void qemu_screen_warp_pointer(QemuScreen *obj, int x, int y); + +G_END_DECLS + +#endif diff -r 161ce6660801 -r 1f7d5a9d2623 gtk/qemu.override --- /dev/null Sun Jul 16 17:46:42 2006 +++ b/gtk/qemu.override Sun Jul 16 17:47:31 2006 @@ -0,0 +1,13 @@ +%% +headers +#include <Python.h> +#include "pygobject.h" +#include "qemu-screen.h" +%% +modulename qemu +%% +import gtk.DrawingArea as PyGtkDrawingArea_Type +%% +ignore-glob + *_get_type +%% \ No newline at end of file
_______________________________________________ Qemu-devel mailing list Qemu-devel@nongnu.org http://lists.nongnu.org/mailman/listinfo/qemu-devel