diff options
author | kraxel <kraxel> | 2007-08-17 16:00:51 +0000 |
---|---|---|
committer | kraxel <kraxel> | 2007-08-17 16:00:51 +0000 |
commit | bc328e784fdbe3afebbb28253b4f2ad2c1c15eb8 (patch) | |
tree | bf9fa5e7cf1b6b7d8d9ff7ff73a72dd47dfddcfa | |
parent | eccfb31d8a10d756f33454a5eca97083d246c6ad (diff) | |
download | xenwatch-bc328e784fdbe3afebbb28253b4f2ad2c1c15eb8.tar.gz |
switch to gtk-vnc widget
-rw-r--r-- | GNUmakefile | 15 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | vnc-old.c | 1379 | ||||
-rw-r--r-- | vnc.c | 1095 |
4 files changed, 1486 insertions, 1005 deletions
diff --git a/GNUmakefile b/GNUmakefile index 857a7d8..d8a083f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -9,13 +9,13 @@ CFLAGS += -DVERSION='"$(VERSION)"' -DLIB='"$(LIB)"' TARGETS := xenlog xenscreen BUILD_GTK := xenwatch xenstore mdns-browser BUILD_AVAHI := mdns-publish-xendom mdns-publish-vnc -BUILD_VNCCLIENT := vnc-client +BUILD_GTK_VNC := vnc-client NEEDS_XENSTORE := xenlog xenscreen xenwatch xenstore mdns-publish-xendom mdns-publish-vnc NEEDS_LIBVIRT := xenscreen NEEDS_GTK := xenwatch xenstore mdns-browser vnc-client NEEDS_AVAHI := mdns-browser mdns-publish-xendom mdns-publish-vnc -NEEDS_VNCCLIENT := xenwatch mdns-browser vnc-client +NEEDS_GTK_VNC := xenwatch mdns-browser vnc-client # default target all: build @@ -31,9 +31,8 @@ LIB := $(LIB) HAVE_GTK := $(call ac_pkg_config,gtk+-x11-2.0) HAVE_AVAHI := $(call ac_pkg_config,avahi-glib) HAVE_LIBVIRT := $(call ac_pkg_config,libvirt) +HAVE_GTK_VNC := $(call ac_pkg_config,gtk-vnc-1.0) HAVE_XENSTORE := $(call ac_lib,xs_daemon_open,xenstore) -HAVE_VNCCLIENT := $(call ac_lib,rfbGetClient,vncclient,-lz -ljpeg) -HAVE_VNC_TEXT := $(call ac_lib,TextChatSend,vncclient,-lz -ljpeg) endef @@ -42,9 +41,9 @@ ifeq ($(HAVE_GTK),yes) $(NEEDS_GTK) : CFLAGS += -Wno-strict-prototypes $(NEEDS_GTK) : pkglst += gtk+-x11-2.0 TARGETS += $(BUILD_GTK) - ifeq ($(HAVE_VNCCLIENT),yes) - TARGETS += $(BUILD_VNCCLIENT) - $(NEEDS_VNCCLIENT) : LDLIBS += -lvncclient -lz -ljpeg -lGL + ifeq ($(HAVE_GTK_VNC),yes) + TARGETS += $(BUILD_GTK_VNC) + $(NEEDS_GTK_VNC) : pkglst += gtk-vnc-1.0 endif endif @@ -62,7 +61,7 @@ ifeq ($(HAVE_LIBVIRT),yes) endif # compile flags -CFLAGS += $(call ac_inc_cflags,XENSTORE LIBVIRT AVAHI VNCCLIENT VNC_TEXT) +CFLAGS += $(call ac_inc_cflags,XENSTORE LIBVIRT AVAHI GTK_VNC) CFLAGS += $(shell test "$(pkglst)" != "" && pkg-config --cflags $(pkglst)) LDLIBS += $(shell test "$(pkglst)" != "" && pkg-config --libs $(pkglst)) @@ -1 +1 @@ -0.2 +0.3 diff --git a/vnc-old.c b/vnc-old.c new file mode 100644 index 0000000..3fc3d65 --- /dev/null +++ b/vnc-old.c @@ -0,0 +1,1379 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <locale.h> +#include <signal.h> + +#include <X11/X.h> +#include <X11/Xlib.h> + +#include <GL/gl.h> +#include <GL/glx.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include "x11.h" +#include "vnc.h" + +#ifdef HAVE_VNCCLIENT + +#include <rfb/rfbclient.h> + +static int debug_libvnc; + +/* ------------------------------------------------------------------ */ + +struct pos { + int x; + int y; +}; + +struct rect { + int w; + int h; +}; + +struct vnc_window { + /* vnc connection */ + char display[128]; + rfbClient *client; + GIOChannel *ch; + guint id, connected; + + /* gtk */ + GtkAccelGroup *ac; + GtkActionGroup *ag; + GtkUIManager *ui; + GtkWidget *win; + GtkWidget *draw; + GtkWidget *line, *res, *mbutton, *popup; + GdkCursor *on,*off; + int filter_installed; + int input_grabbed; + + /* x11 */ + XImage *ximage; + void *shm; + GC gc; + Display *dpy; + unsigned char keydown[32]; + + /* opengl */ + int have_gl; + GLuint tex; + int tex_max; + unsigned char *tex_data; + unsigned int dirty_y1, dirty_y2; + + /* window / vnc display config */ + struct rect window; + struct pos vncoff; + struct rect vncdpy; + struct rect texture; + int updates, redraw; + + /* config */ + int fullscreen; + int viewonly; + int standalone; + int showpointer; + int gl_allways; + int gl_fullscreen; + int uskbd; + int debug; +}; + +/* ------------------------------------------------------------------ */ +/* data tables */ + +rfbKeySym linux_uskbd[][2] = { +#include "linux-uskbd.h" +}; +static int linux_uskbd_size = sizeof(linux_uskbd)/sizeof(linux_uskbd[0]); + +/* ------------------------------------------------------------------ */ +/* prototypes */ + +static GdkFilterReturn event_filter(GdkXEvent *gdkxevent, GdkEvent *gtkevent, + gpointer data); +static void vnc_window_conf(struct vnc_window *vnc); +static void vnc_window_texts(struct vnc_window *vnc); + +/* ------------------------------------------------------------------ */ +/* opengl bits */ + +static int gl_error; +static int gl_attrib[] = { GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + None }; + +static int +catch_gl_error(Display * dpy, XErrorEvent * event) +{ + fprintf(stderr,"WARNING: Your OpenGL setup is broken.\n"); + gl_error++; + return 0; +} + +static int gl_init(struct vnc_window *vnc) +{ + Window win = gdk_x11_drawable_get_xid(vnc->draw->window); + Screen *scr = DefaultScreenOfDisplay(vnc->dpy); + void *old_handler; + XVisualInfo *visinfo; + GLXContext ctx; + + if (vnc->debug) + fprintf(stderr, "gl: init [window=0x%lx]\n", win); + if (!win) + return -1; + visinfo = glXChooseVisual(vnc->dpy, XScreenNumberOfScreen(scr), + gl_attrib); + if (!visinfo) { + if (vnc->debug) + fprintf(stderr,"gl: can't get visual (rgb,db)\n"); + return -1; + } + ctx = glXCreateContext(vnc->dpy, visinfo, NULL, True); + if (!ctx) { + if (vnc->debug) + fprintf(stderr,"gl: can't create context\n"); + return -1; + } + + /* there is no point in using OpenGL for image scaling if it + * isn't hardware accelerated ... */ + if (vnc->debug) + fprintf(stderr, "gl: DRI=%s\n", + glXIsDirect(vnc->dpy, ctx) ? "Yes" : "No"); + if (!glXIsDirect(vnc->dpy, ctx)) + return -1; + + old_handler = XSetErrorHandler(catch_gl_error); + glXMakeCurrent(vnc->dpy, win, ctx); + XSync(vnc->dpy, False); + XSetErrorHandler(old_handler); + if (gl_error) + return -1; + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &vnc->tex_max); + if (vnc->debug) + fprintf(stderr, "gl: texture max size: %d\n", vnc->tex_max); + return 0; +} + +static void gl_resize_window(struct vnc_window *vnc) +{ + if (!vnc->tex) + return; + + glClearColor (0.0, 0.0, 0.0, 0.0); + glShadeModel(GL_FLAT); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glViewport(0, 0, vnc->window.w, vnc->window.h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, vnc->window.w, 0.0, vnc->window.h, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + vnc->redraw++; +} + +static void gl_blit(struct vnc_window *vnc, int y1, int y2) +{ + Window win = gdk_x11_drawable_get_xid(vnc->draw->window); + float x,y; + unsigned int ww = vnc->window.w, wh = vnc->window.h; + int wx = 0, wy = 0; + + if (!vnc->tex) + return; + + if (y1 > vnc->vncdpy.h) + y1 = vnc->vncdpy.h; + if (y2 > vnc->vncdpy.h) + y2 = vnc->vncdpy.h; + + glBindTexture(GL_TEXTURE_2D, vnc->tex); + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0,y1, vnc->vncdpy.w, y2 - y1, + GL_BGRA_EXT /* GL_RGB */, + GL_UNSIGNED_BYTE, + vnc->tex_data + y1 * 4 * vnc->vncdpy.w); + x = (float)vnc->vncdpy.w / vnc->texture.w; + y = (float)vnc->vncdpy.h / vnc->texture.h; + + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glBegin(GL_QUADS); + glTexCoord2f(0,y); glVertex3f(wx, wy, 0); + glTexCoord2f(0,0); glVertex3f(wx, wy+wh, 0); + glTexCoord2f(x,0); glVertex3f(wx+ww, wy+wh, 0); + glTexCoord2f(x,y); glVertex3f(wx+ww, wy, 0); + glEnd(); + glXSwapBuffers(vnc->dpy, win); + glDisable(GL_TEXTURE_2D); +} + +static void gl_update_vnc(struct vnc_window *vnc, + int x, int y, int w, int h) +{ + if (vnc->dirty_y1 > y) + vnc->dirty_y1 = y; + if (vnc->dirty_y2 < y+h) + vnc->dirty_y2 = y+h; + vnc->updates++; +} + +static void gl_update_win(struct vnc_window *vnc, + int x, int y, int w, int h) +{ + vnc->dirty_y1 = 0; + vnc->dirty_y2 = vnc->vncdpy.h; + vnc->updates++; +} + +static void gl_flush(struct vnc_window *vnc) +{ + if (vnc->redraw) { + vnc->dirty_y1 = 0; + vnc->dirty_y2 = vnc->vncdpy.h; + } + gl_blit(vnc, vnc->dirty_y1, vnc->dirty_y2); + + vnc->dirty_y1 = 99999; + vnc->dirty_y2 = 0; + vnc->updates = 0; + vnc->redraw = 0; +} + +/* ------------------------------------------------------------------ */ +/* x11 draw bits */ + +static void x11_update_win(struct vnc_window *vnc, + int x, int y, int w, int h) +{ + Window win = gdk_x11_drawable_get_xid(vnc->draw->window); + + if (!vnc->gc) { + XGCValues values; + XColor color, dummy; + Colormap cmap; + + cmap = gdk_x11_colormap_get_xcolormap(gtk_widget_get_colormap(vnc->win)); + XAllocNamedColor(vnc->dpy, cmap, "#404040", &color, &dummy); + values.function = GXcopy; + values.foreground = color.pixel; + vnc->gc = XCreateGC(vnc->dpy, win, + GCForeground|GCFunction, + &values); + } + + if (x < vnc->vncoff.x) + vnc->redraw++; + if (y < vnc->vncoff.y) + vnc->redraw++; + if (x+w > vnc->vncoff.x + vnc->vncdpy.w) + vnc->redraw++; + if (y+h > vnc->vncoff.y + vnc->vncdpy.h) + vnc->redraw++; + + if (vnc->redraw) { + XFillRectangle(vnc->dpy, win, vnc->gc, + 0,0, vnc->window.w, vnc->window.h); + XPUTIMAGE(vnc->dpy, win, vnc->gc, vnc->ximage, 0,0, + vnc->vncoff.x, vnc->vncoff.y, + vnc->vncdpy.w, vnc->vncdpy.h); + vnc->redraw = 0; + } else { + XPUTIMAGE(vnc->dpy, win, vnc->gc, vnc->ximage, + x-vnc->vncoff.x, y-vnc->vncoff.y, + x,y, w,h); + } +} + +static void x11_update_vnc(struct vnc_window *vnc, + int x, int y, int w, int h) +{ + x11_update_win(vnc, x + vnc->vncoff.x, y + vnc->vncoff.y, w, h); +} + +static void x11_resize_window(struct vnc_window *vnc) +{ + vnc->vncoff.x = (vnc->window.w - vnc->vncdpy.w) / 2; + vnc->vncoff.y = (vnc->window.h - vnc->vncdpy.h) / 2; +} + +static void x11_flush(struct vnc_window *vnc) +{ + if (vnc->redraw) + x11_update_win(vnc, 0, 0, vnc->vncdpy.w, vnc->vncdpy.w); + vnc->updates = 0; + vnc->redraw = 0; +} + +/* ------------------------------------------------------------------ */ +/* x11/gl wrappers */ + +/* vnc display coordinates */ +static void dpy_update_vnc(struct vnc_window *vnc, + int x, int y, int w, int h) +{ + if (vnc->debug) + fprintf(stderr, "%s: mode%s%s, %dx%d+%d+%d\n", __FUNCTION__, + vnc->tex ? " GL" : "", + vnc->ximage ? " X11" : "", + w, h, x, y); + if (vnc->ximage) + x11_update_vnc(vnc, x, y, w, h); + if (vnc->tex) + gl_update_vnc(vnc, x, y, w, h); +} + +/* app window coordinates */ +static void dpy_update_win(struct vnc_window *vnc, + int x, int y, int w, int h) +{ + if (vnc->debug) + fprintf(stderr, "%s: mode%s%s, %dx%d+%d+%d\n", __FUNCTION__, + vnc->tex ? " GL" : "", + vnc->ximage ? " X11" : "", + w, h, x, y); + if (vnc->ximage) + x11_update_win(vnc, x, y, w, h); + if (vnc->tex) + gl_update_win(vnc, x, y, w, h); +} + +static void dpy_redraw(struct vnc_window *vnc) +{ + vnc->redraw++; +} + +static void dpy_flush(struct vnc_window *vnc, const char *caller) +{ + if (vnc->debug) + fprintf(stderr, "%s: from %s, mode%s%s, updates %d, redraw %d\n", + __FUNCTION__, caller, + vnc->tex ? " GL" : "", + vnc->ximage ? " X11" : "", + vnc->updates, vnc->redraw); + if (!vnc->updates && !vnc->redraw) + return; + + if (vnc->ximage) + x11_flush(vnc); + if (vnc->tex) + gl_flush(vnc); +} + +static int dpy_gl_check(struct vnc_window *vnc) +{ + int using_gl = 0; + + if (vnc->have_gl && vnc->vncdpy.w < vnc->tex_max && vnc->vncdpy.h < vnc->tex_max) { + if (vnc->gl_allways) + using_gl = 1; + if (vnc->gl_fullscreen && vnc->fullscreen) + using_gl = 1; + } + return using_gl; +} + +static void dpy_setup(struct vnc_window *vnc, int width, int height, int using_gl) +{ + /* cleanup */ + if (vnc->ximage) { + x11_destroy_ximage(vnc->dpy, vnc->ximage, vnc->shm); + vnc->ximage = NULL; + } + if (vnc->tex) { + /* FIXME: release texture */ + vnc->tex = 0; + vnc->vncdpy.w = 0; + vnc->vncdpy.h = 0; + free(vnc->tex_data); + vnc->tex_data = NULL; + } + + vnc->vncdpy.w = width; + vnc->vncdpy.h = height; + + if (!using_gl) { + /* init X11 */ + vnc->ximage = x11_create_ximage(vnc->dpy, vnc->vncdpy.w, vnc->vncdpy.h, &vnc->shm); + if (NULL == vnc->ximage) { + fprintf(stderr, "Oops: creating ximage failed\n"); + return; + } + + vnc->client->width = + vnc->ximage->bytes_per_line / (vnc->ximage->bits_per_pixel / 8); + vnc->client->frameBuffer = (void*)vnc->ximage->data; + + vnc->client->format.bitsPerPixel = vnc->ximage->bits_per_pixel; + vnc->client->format.redShift = x11_red_shift; + vnc->client->format.greenShift = x11_green_shift; + vnc->client->format.blueShift = x11_blue_shift; + vnc->client->format.redMax = (1 << x11_red_bits) - 1; + vnc->client->format.greenMax = (1 << x11_green_bits) - 1; + vnc->client->format.blueMax = (1 << x11_blue_bits) - 1; + } else { + /* init OpenGL */ + void *dummy; + int i; + + /* figure texture size (power of two) */ + for (i = 0; vnc->vncdpy.w >= (1 << i); i++) + ; + vnc->texture.w = (1 << i); + for (i = 0; vnc->vncdpy.h >= (1 << i); i++) + ; + vnc->texture.h = (1 << i); + if (vnc->debug) + fprintf(stderr,"%s: client %dx%d, tex %dx%d\n", __FUNCTION__, + vnc->vncdpy.w, vnc->vncdpy.h, vnc->texture.w, vnc->texture.h); + + /* create texture */ + glGenTextures(1, &vnc->tex); + glBindTexture(GL_TEXTURE_2D, vnc->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + dummy = malloc(vnc->texture.w * vnc->texture.h * 4); + memset(dummy, 128, vnc->texture.w * vnc->texture.h * 4); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + vnc->texture.w, vnc->texture.h, 0, + GL_RGB, GL_UNSIGNED_BYTE, + dummy); + free(dummy); + + /* image buffer */ + vnc->tex_data = malloc(vnc->vncdpy.w * vnc->vncdpy.h * 4); + vnc->client->frameBuffer = vnc->tex_data; + + vnc->client->format.bitsPerPixel = 32; + vnc->client->format.redShift = 16; + vnc->client->format.greenShift = 8; + vnc->client->format.blueShift = 0; + vnc->client->format.redMax = 255; + vnc->client->format.greenMax = 255; + vnc->client->format.blueMax = 255; + } + + if (vnc->debug) + fprintf(stderr, "%s: SetFormatAndEncodings: %s\n", __FUNCTION__, + using_gl ? "GL" : "X11" ); + SetFormatAndEncodings(vnc->client); + SendFramebufferUpdateRequest(vnc->client, 0, 0, + vnc->vncdpy.w, vnc->vncdpy.h, False); + vnc_window_conf(vnc); +} + +static void dpy_resize_window(struct vnc_window *vnc) +{ + int using_gl; + + vnc->vncoff.x = 0; + vnc->vncoff.y = 0; + using_gl = dpy_gl_check(vnc); + if ((vnc->tex && !using_gl) || + (vnc->ximage && using_gl)) { + /* switching display mode */ + dpy_setup(vnc, vnc->client->width, vnc->client->height, using_gl); + } else { + if (vnc->ximage) + x11_resize_window(vnc); + if (vnc->tex) + gl_resize_window(vnc); + dpy_redraw(vnc); + } + vnc_window_texts(vnc); +} + +/* ------------------------------------------------------------------ */ +/* helper functions */ + +static void XAddInput(Display *dpy, Window win, long mask) +{ + XWindowAttributes attr; + + XGetWindowAttributes(dpy, win, &attr); + XSelectInput(dpy, win, attr.your_event_mask | mask); +} + +static GdkCursor* empty_cursor(void) +{ + static char bits[32]; + GdkCursor *cursor; + GdkPixmap *pixmap; + GdkColor fg = { 0, 0, 0, 0 }; + GdkColor bg = { 0, 0, 0, 0 }; + + pixmap = gdk_bitmap_create_from_data(NULL, bits, 16, 16); + cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &fg, &bg, 0, 0); + gdk_pixmap_unref(pixmap); + return cursor; +} + +static void vnc_window_texts(struct vnc_window *vnc) +{ + char textline[256]; + int pos; + + if (vnc->client && vnc->client->desktopName && + strlen(vnc->client->desktopName)) { + gtk_window_set_title(GTK_WINDOW(vnc->win), vnc->client->desktopName); + } else { + snprintf(textline, sizeof(textline), "connecting to %s ...", + vnc->display); + gtk_window_set_title(GTK_WINDOW(vnc->win), textline); + } + + if (vnc->input_grabbed) { + gtk_label_set_text(GTK_LABEL(vnc->line), + "Press Ctrl-Alt to release input grab."); + } else if (vnc->client) { + snprintf(textline, sizeof(textline), "VNC: \"%s\" at %s", + vnc->client->desktopName ?: "", vnc->display); + gtk_label_set_text(GTK_LABEL(vnc->line), textline); + } + + if (vnc->client) { + pos = 0; + pos += snprintf(textline+pos, sizeof(textline)-pos, "%dx%d", + vnc->vncdpy.w, vnc->vncdpy.h); + if (vnc->vncdpy.w != vnc->window.w || + vnc->vncdpy.h != vnc->window.h) + pos += snprintf(textline+pos, sizeof(textline)-pos, " @ %dx%d", + vnc->window.w, vnc->window.h); + pos += snprintf(textline+pos, sizeof(textline)-pos, " (%s)", + vnc->ximage ? "X11" : "GL"); + gtk_label_set_text(GTK_LABEL(vnc->res), textline); + } +} + +static void vnc_window_conf(struct vnc_window *vnc) +{ + if (!vnc->draw) + return; + if (vnc->client) + gtk_widget_set_size_request(vnc->draw, vnc->vncdpy.w, vnc->vncdpy.h); + if (vnc->draw->window) { + gdk_window_set_cursor(vnc->draw->window, + vnc->showpointer ? vnc->on : vnc->off); + /* FIXME */ + XAddInput(vnc->dpy, gdk_x11_drawable_get_xid(vnc->draw->window), + KeymapStateMask); + } + dpy_resize_window(vnc); +} + +static void vnc_release(struct vnc_window *vnc) +{ + int sock = -1; + + if (NULL == vnc) + return; + if (vnc->connected && vnc->client) { + /* + * library bugs? + * - calling rfbClientCleanup() unconnected segfaults + * - rfbClientCleanup() doesn't close the socket + */ + sock = vnc->client->sock; + rfbClientCleanup(vnc->client); + vnc->client = NULL; + } + if (vnc->filter_installed) + gdk_window_remove_filter(vnc->draw->window, event_filter, vnc); + if (vnc->id) + g_source_destroy(g_main_context_find_source_by_id + (g_main_context_default(), vnc->id)); + if (vnc->ximage) + x11_destroy_ximage(vnc->dpy, vnc->ximage, vnc->shm); + if (vnc->gc) + XFreeGC(vnc->dpy, vnc->gc); + if (-1 != sock) + close(sock); + free(vnc); +} + +static void grab_input(struct vnc_window *vnc, guint32 time) +{ + if (vnc->viewonly) + return; + if (vnc->input_grabbed) + return; + gdk_pointer_grab(vnc->draw->window, + FALSE, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK, + vnc->draw->window, + NULL, + time); + gdk_keyboard_grab(vnc->draw->window, + FALSE, + time); + vnc->input_grabbed = 1; + vnc_window_texts(vnc); +} + +static void ungrab_input(struct vnc_window *vnc, guint32 time) +{ + if (!vnc->input_grabbed) + return; + gdk_pointer_ungrab(time); + gdk_keyboard_ungrab(time); + vnc->input_grabbed = 0; + vnc_window_texts(vnc); +} + +/* ------------------------------------------------------------------ */ +/* libvncclient callbacks */ + +static rfbBool vnc_resize(rfbClient* client) +{ + struct vnc_window *vnc = rfbClientGetClientData(client, vnc_open); + + if (vnc->debug) + fprintf(stderr, "%s: %dx%d\n", + __FUNCTION__, client->width, client->height); + if (vnc->vncdpy.w == client->width && + vnc->vncdpy.h == client->height) { + if (vnc->debug) + fprintf(stderr, "%s: no size change, early exit\n", __FUNCTION__); + return TRUE; + } + + dpy_setup(vnc, client->width, client->height, dpy_gl_check(vnc)); + return TRUE; +} + +static void vnc_update(rfbClient* cl, int x, int y, int w, int h) +{ + struct vnc_window *vnc = rfbClientGetClientData(cl, vnc_open); + + if (!GTK_WIDGET_DRAWABLE(vnc->draw)) + return; + dpy_update_vnc(vnc, x,y, w,h); +} + +#ifdef HAVE_VNC_TEXT +static void vnc_textchat(rfbClient* cl, int value, char *text) +{ + switch(value) { + case rfbTextChatOpen: + fprintf(stderr,"%s: Open\n", __FUNCTION__); + break; + case rfbTextChatClose: + case rfbTextChatFinished: + fprintf(stderr,"%s: Close/Finished\n", __FUNCTION__); + break; + default: + fprintf(stderr,"%s: \"%s\"\n", __FUNCTION__, text); + break; + } +} +#endif + +static char *vnc_passwd(rfbClient* cl) +{ + struct vnc_window *vnc = rfbClientGetClientData(cl, vnc_open); + GtkWidget *dialog, *label, *entry; + char *passwd = NULL; + const char *txt; + char message[256]; + + if (vnc->debug) + fprintf(stderr,"%s: called\n", __FUNCTION__); + + /* Create the widgets */ + dialog = gtk_dialog_new_with_buttons("Password needed", + vnc->win ? GTK_WINDOW(vnc->win) : NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + NULL); + + snprintf(message, sizeof(message), + "Please enter vnc screen password for \"%s\".", + vnc->client->desktopName ? : "unknown"); + label = gtk_label_new(message); + entry = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 10); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + + /* show and wait for response */ + gtk_widget_show_all(dialog); + switch (gtk_dialog_run(GTK_DIALOG(dialog))) { + case GTK_RESPONSE_ACCEPT: + txt = gtk_entry_get_text(GTK_ENTRY(entry)); + passwd = strdup(txt); + if (vnc->debug) + fprintf(stderr,"%s: OK: \"%s\"\n", __FUNCTION__, passwd); + break; + default: + if (vnc->debug) + fprintf(stderr,"%s: canceled\n", __FUNCTION__); + passwd = strdup(""); + break; + } + gtk_widget_destroy(dialog); + + return passwd; +} + +static void vnc_log(const char *format, ...) +{ + va_list args; + + if (!debug_libvnc) + return; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +/* ------------------------------------------------------------------ */ +/* glib/gtk callbacks */ + +static gboolean vnc_data_cb(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + struct vnc_window *vnc = data; + + if (!HandleRFBServerMessage(vnc->client)) { + /* server closed connection */ + g_source_destroy(g_main_context_find_source_by_id + (g_main_context_default(), vnc->id)); + vnc->id = 0; + gtk_widget_destroy(vnc->win); + } + dpy_flush(vnc, __FUNCTION__); + return TRUE; +} + +static void destroy_cb(GtkWidget *widget, gpointer data) +{ + struct vnc_window *vnc = data; + + if (vnc->debug) + fprintf(stderr,"%s: called\n", __FUNCTION__); + if (vnc->standalone) + gtk_main_quit(); + vnc_release(vnc); +} + +static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + struct vnc_window *vnc = data; + + dpy_update_win(vnc, event->area.x, event->area.y, + event->area.width, event->area.height); + if (0 == event->count) + dpy_flush(vnc, __FUNCTION__); + return TRUE; +} + +static gboolean configure_cb(GtkWidget *widget, GdkEventConfigure *event, + gpointer data) +{ + struct vnc_window *vnc = data; + + if (vnc->debug) + fprintf(stderr,"%s: %dx%d\n", __FUNCTION__, + event->width, event->height); + vnc->window.w = event->width; + vnc->window.h = event->height; + dpy_resize_window(vnc); + dpy_flush(vnc, __FUNCTION__); + return TRUE; +} + +static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, + gpointer data) +{ + struct vnc_window *vnc = data; + GtkWidget *item; + + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { + vnc->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; + if (vnc->debug) + fprintf(stderr, "%s: fullscreen %s\n", __FUNCTION__, + vnc->fullscreen ? "on" : "off"); + item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/FullScreen"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->fullscreen); + } + return TRUE; +} + +static void send_mouse(struct vnc_window *vnc, int x, int y, + int x11state, int x11press, int x11release) +{ + static const struct { + int rfbmask; + int x11mask; + int x11nr; + } buttons[] = { + { + .rfbmask = rfbButton1Mask, + .x11mask = Button1Mask, + .x11nr = Button1, + },{ + .rfbmask = rfbButton2Mask, + .x11mask = Button2Mask, + .x11nr = Button2, + },{ + .rfbmask = rfbButton3Mask, + .x11mask = Button3Mask, + .x11nr = Button3, + },{ + .rfbmask = rfbButton4Mask, + .x11mask = Button4Mask, + .x11nr = Button4, + },{ + .rfbmask = rfbButton5Mask, + .x11mask = Button5Mask, + .x11nr = Button5, + } + }; + int i, rfbstate = 0; + + if (vnc->viewonly) + return; + + for (i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++) { + if (x11state & buttons[i].x11mask) + rfbstate |= buttons[i].rfbmask; + if (x11press == buttons[i].x11nr) + rfbstate |= buttons[i].rfbmask; + if (x11release == buttons[i].x11nr) + rfbstate &= ~buttons[i].rfbmask; + } + + /* fixup mouse coordinates */ + x -= vnc->vncoff.x; + y -= vnc->vncoff.y; + if (vnc->tex) { + x = x * vnc->window.w / vnc->vncdpy.w; + y = y * vnc->window.h / vnc->vncdpy.h; + } + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if (x >= vnc->vncdpy.w) + x = vnc->vncdpy.w -1; + if (y >= vnc->vncdpy.h) + y = vnc->vncdpy.h -1; + + if (vnc->debug) + fprintf(stderr,"%s: +%d+%d x11state 0x%x rfbstate 0x%x\n", + __FUNCTION__, x, y, x11state, rfbstate); + SendPointerEvent(vnc->client, x, y, rfbstate); +} + +static gboolean button_cb(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + struct vnc_window *vnc = data; + switch (event->type) { + case GDK_BUTTON_PRESS: + send_mouse(vnc, event->x, event->y, event->state, event->button, 0); + grab_input(vnc, event->time); + break; + case GDK_BUTTON_RELEASE: + send_mouse(vnc, event->x, event->y, event->state, 0, event->button); + break; + default: + /* keep gcc happy */ + break; + } + return TRUE; +} + +static gboolean motion_cb(GtkWidget *widget, GdkEventMotion *event, + gpointer data) +{ + struct vnc_window *vnc = data; + send_mouse(vnc, event->x, event->y, event->state, 0, 0); + return TRUE; +} + +static void key_local(struct vnc_window *vnc, GdkEventKey *event) +{ + int keydown; + + if (vnc->debug) + fprintf(stderr,"%s[%d]: called: keysym %d\n", __FUNCTION__, + event->type, event->keyval); + keydown = (8 == event->type); + if (!vnc->viewonly) + SendKeyEvent(vnc->client, event->keyval, keydown ? TRUE : FALSE); +} + +static void key_uskbd(struct vnc_window *vnc, GdkEventKey *event) +{ + rfbKeySym keysym = 0; + int shift,keydown; + + keydown = (8 == event->type); + if (event->hardware_keycode < 256) { + int by = event->hardware_keycode / 8; + int bi = event->hardware_keycode % 8; + if (keydown) + vnc->keydown[by] |= (1 << bi); + else + vnc->keydown[by] &= ~(1 << bi); + } + + shift = (event->state & GDK_SHIFT_MASK) ? 1 : 0; + if (event->hardware_keycode < linux_uskbd_size) { + keysym = linux_uskbd[event->hardware_keycode][shift]; + if (0 == keysym) + keysym = linux_uskbd[event->hardware_keycode][0]; + } + + if (vnc->debug || 0 == keysym) + fprintf(stderr,"%s[%d]: called: keycode %d => keysym %d\n", __FUNCTION__, + event->type, event->hardware_keycode, keysym); + if (keysym && !vnc->viewonly) + SendKeyEvent(vnc->client, keysym, keydown ? TRUE : FALSE); +} + +static gboolean key_cb(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + struct vnc_window *vnc = data; + int mask = GDK_CONTROL_MASK | GDK_MOD1_MASK; + + if (mask == (event->state & mask)) + ungrab_input(vnc, event->time); + + if (vnc->uskbd) + key_uskbd(vnc, event); + else + key_local(vnc, event); + return TRUE; +} + +static void menu_btn(GtkWidget *widget, gpointer data) +{ + struct vnc_window *vnc = data; + + gtk_menu_popup(GTK_MENU(vnc->popup), NULL, NULL, NULL, NULL, + 0, gtk_get_current_event_time()); +} + +static GdkFilterReturn event_filter(GdkXEvent *gdkxevent, GdkEvent *gtkevent, + gpointer data) +{ + struct vnc_window *vnc = data; + XEvent *xevent = gdkxevent; + int by, bi, keydown, keycode; + rfbKeySym keysym = 0; + + switch (xevent->type) { + case KeymapNotify: + if (!vnc->uskbd) + return GDK_FILTER_REMOVE; + if (!GTK_WIDGET_HAS_FOCUS(vnc->draw)) + return GDK_FILTER_REMOVE; + for (by = 0; by < 32; by++) { + if (vnc->keydown[by] == xevent->xkeymap.key_vector[by]) + continue; + for (bi = 0; bi < 8; bi++) { + if ((vnc->keydown[by] & (1 << bi)) == + (xevent->xkeymap.key_vector[by] & (1 << bi))) + continue; + keydown = xevent->xkeymap.key_vector[by] & (1 << bi); + keycode = by * 8 + bi; + keysym = linux_uskbd[keycode][0]; + if (!keysym) + continue; + if (vnc->debug) + fprintf(stderr,"%s: KeymapNotify: %-7s %3d\n", __FUNCTION__, + keydown ? "press" : "release", keycode); + if (!vnc->viewonly) + SendKeyEvent(vnc->client, keysym, keydown ? TRUE : FALSE); + } + } + memcpy(vnc->keydown, xevent->xkeymap.key_vector, 32); + return GDK_FILTER_REMOVE; + default: + return GDK_FILTER_CONTINUE; + } +} + +/* ------------------------------------------------------------------ */ + +static void menu_cb_full_screen(GtkToggleAction *action, gpointer user_data) +{ + struct vnc_window *vnc = user_data; + + vnc->fullscreen = gtk_toggle_action_get_active(action); + if (vnc->fullscreen) + gtk_window_fullscreen(GTK_WINDOW(vnc->win)); + else + gtk_window_unfullscreen(GTK_WINDOW(vnc->win)); +} + +static void menu_cb_us_keyboard(GtkToggleAction *action, gpointer user_data) +{ + struct vnc_window *vnc = user_data; + + vnc->uskbd = gtk_toggle_action_get_active(action); +} + +static void menu_cb_show_pointer(GtkToggleAction *action, gpointer user_data) +{ + struct vnc_window *vnc = user_data; + + vnc->showpointer = gtk_toggle_action_get_active(action); + if (vnc->draw->window) + gdk_window_set_cursor(vnc->draw->window, + vnc->showpointer ? vnc->on : vnc->off); +} + +static void menu_cb_view_only(GtkToggleAction *action, gpointer user_data) +{ + struct vnc_window *vnc = user_data; + + vnc->viewonly = gtk_toggle_action_get_active(action); +} + +static void menu_cb_gl(GtkRadioAction *action, GtkRadioAction *current, + gpointer user_data) +{ + struct vnc_window *vnc = user_data; + int value = gtk_radio_action_get_current_value(action); + + switch (value) { + case 1: /* OFF */ + vnc->gl_fullscreen = 0; + vnc->gl_allways = 0; + break; + case 2: /* fullscreen ON */ + vnc->gl_fullscreen = 1; + vnc->gl_allways = 0; + break; + case 3: /* allways ON */ + vnc->gl_fullscreen = 1; + vnc->gl_allways = 1; + break; + } + dpy_resize_window(vnc); +} + +static void menu_cb_about(GtkMenuItem *item, void *user_data) +{ + static char *comments = "simple vnc client"; + static char *copyright = "(c) 2005-2007 Gerd Hoffmann"; + static char *authors[] = { "Gerd Hoffmann <kraxel@redhat.com>", NULL }; + struct vnc_window *vnc = user_data; + + gtk_show_about_dialog(GTK_WINDOW(vnc->win), + "authors", authors, + "comments", comments, + "copyright", copyright, + "logo-icon-name", GTK_STOCK_ABOUT, + "version", VERSION, + NULL); +} + +static void menu_cb_quit(GtkMenuItem *item, void *user_data) +{ + struct vnc_window *vnc = user_data; + + gtk_widget_destroy(vnc->win); +} + +/* ------------------------------------------------------------------ */ + +static const GtkActionEntry entries[] = { + { + /* popup menu */ + .name = "ConfMenu", + .label = "Config", + },{ + /* menu items */ + .name = "About", + .stock_id = GTK_STOCK_ABOUT, + .label = "_About ...", + .callback = G_CALLBACK(menu_cb_about), + },{ + .name = "Close", + .stock_id = GTK_STOCK_QUIT, + .label = "_Close", +// .accelerator = "<control>Q", + .tooltip = "Quit the job", + .callback = G_CALLBACK(menu_cb_quit), + } +}; + +static const GtkToggleActionEntry tentries[] = { + { + .name = "FullScreen", + .label = "_Fullscreen", + .accelerator = "F11", + .callback = G_CALLBACK(menu_cb_full_screen), + },{ + .name = "USKbd", + .label = "US _Keyboard", + .callback = G_CALLBACK(menu_cb_us_keyboard), + },{ + .name = "ShowPointer", + .label = "Show _Pointer", + .callback = G_CALLBACK(menu_cb_show_pointer), + },{ + .name = "ViewOnly", + .label = "_View only", + .callback = G_CALLBACK(menu_cb_view_only), + } +}; + +static const GtkRadioActionEntry rentries[] = { + { + .name = "GL_OFF", + .label = "_Disable OpenGL scaling", + .value = 1, + },{ + .name = "GL_FSonly", + .label = "OpenGL scaling for fullscreen", + .value = 2, + },{ + .name = "GL_ON", + .label = "Allways scale using _OpenGL", + .value = 3, + } +}; + +static char ui_xml[] = +"<ui>" +" <popup action='ConfMenu'>" +" <menuitem action='FullScreen'/>" +" <menuitem action='USKbd'/>" +" <menuitem action='ShowPointer'/>" +" <menuitem action='ViewOnly'/>" +" <separator/>" +" <menuitem action='GL_OFF'/>" +" <menuitem action='GL_FSonly'/>" +" <menuitem action='GL_ON'/>" +" <separator/>" +" <menuitem action='About'/>" +" <menuitem action='Close'/>" +" </popup>" +"</ui>"; + +/* ------------------------------------------------------------------ */ +/* public API functions */ + +GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, + int debug_level) +{ + GtkWidget *vbox, *hbox, *frame, *item; + GtkAction *action; + GError *err; + char *argv[] = { "vnc-client", NULL, NULL }; + int argc = sizeof(argv)/sizeof(argv[0]) -1; + struct vnc_window *vnc; + int rc; + + vnc = malloc(sizeof(*vnc)); + if (NULL == vnc) + goto err; + memset(vnc,0,sizeof(*vnc)); + vnc->standalone = (flags & VNC_FLAG_STANDALONE); + vnc->showpointer = (flags & VNC_FLAG_SHOW_MOUSE); + vnc->uskbd = (flags & VNC_FLAG_US_KBD); + vnc->viewonly = (flags & VNC_FLAG_VIEW_ONLY); + vnc->gl_allways = (flags & VNC_FLAG_GL_ALLWAYS); + vnc->gl_fullscreen = (flags & VNC_FLAG_GL_FULLSCR); + vnc->debug = debug_level; + debug_libvnc = debug_level; + + snprintf(vnc->display, sizeof(vnc->display), + "%s:%d", hostname, tcpport - 5900); + vnc->dirty_y1 = 99999; + vnc->dirty_y2 = 0; + + /* x11 */ + vnc->dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); + if (NULL == x11_info) + if (0 != x11_color_init(vnc->dpy)) + goto err; + + /* gtk toplevel */ + vnc->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_signal_connect(G_OBJECT(vnc->win), "destroy", + G_CALLBACK(destroy_cb), vnc); + vnc->on = gdk_cursor_new(GDK_LEFT_PTR); + vnc->off = empty_cursor(); + + /* gtk drawing area */ + vnc->draw = gtk_drawing_area_new(); + GTK_WIDGET_SET_FLAGS(vnc->draw, GTK_CAN_FOCUS); + gtk_widget_add_events(vnc->draw, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_EXPOSURE_MASK); + gtk_widget_set_app_paintable(vnc->draw, TRUE); + gtk_widget_set_double_buffered(vnc->draw, FALSE); + g_signal_connect(G_OBJECT(vnc->draw), "expose-event", + G_CALLBACK(expose_cb), vnc); + g_signal_connect(G_OBJECT(vnc->draw), "button-press-event", + G_CALLBACK(button_cb), vnc); + g_signal_connect(G_OBJECT(vnc->draw), "button-release-event", + G_CALLBACK(button_cb), vnc); + g_signal_connect(G_OBJECT(vnc->draw), "motion-notify-event", + G_CALLBACK(motion_cb), vnc); + g_signal_connect(G_OBJECT(vnc->draw), "key-press-event", + G_CALLBACK(key_cb), vnc); + g_signal_connect(G_OBJECT(vnc->draw), "key-release-event", + G_CALLBACK(key_cb), vnc); + g_signal_connect(G_OBJECT(vnc->draw), "configure-event", + G_CALLBACK(configure_cb), vnc); + g_signal_connect(G_OBJECT(vnc->win), "window-state-event", + G_CALLBACK(window_state_cb), vnc); + gdk_window_add_filter(NULL, event_filter, vnc); + vnc->filter_installed = 1; + + /* popup menu */ + vnc->ui = gtk_ui_manager_new(); + vnc->ag = gtk_action_group_new("MenuActions"); + gtk_action_group_add_actions(vnc->ag, entries, G_N_ELEMENTS(entries), vnc); + gtk_action_group_add_toggle_actions(vnc->ag, tentries, + G_N_ELEMENTS(tentries), vnc); + gtk_action_group_add_radio_actions(vnc->ag, rentries, + G_N_ELEMENTS(rentries), + 0, G_CALLBACK(menu_cb_gl), vnc); + gtk_ui_manager_insert_action_group(vnc->ui, vnc->ag, 0); + vnc->ac = gtk_ui_manager_get_accel_group(vnc->ui); + gtk_window_add_accel_group(GTK_WINDOW(vnc->win), vnc->ac); + + err = NULL; + if (!gtk_ui_manager_add_ui_from_string(vnc->ui, ui_xml, -1, &err)) { + g_message("building menus failed: %s", err->message); + g_error_free(err); + exit(1); + } + vnc->popup = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu"); + gtk_menu_set_title(GTK_MENU(vnc->popup), "Config"); + + /* popup menu: initial state */ + item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/USKbd"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->uskbd); + item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/ShowPointer"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->showpointer); + item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/ViewOnly"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->viewonly); + + action = gtk_ui_manager_get_action(vnc->ui, "/ConfMenu/GL_ON"); + if (vnc->gl_allways) + gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 3); + else if (vnc->gl_fullscreen) + gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 2); + else + gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 1); + + /* labels for the status line */ + vnc->line = gtk_label_new("status line"); + vnc->res = gtk_label_new("vnc screen resolution"); + vnc->mbutton = gtk_button_new_with_label("config"); + g_signal_connect(G_OBJECT(vnc->mbutton), "clicked", + G_CALLBACK(menu_btn), vnc); + GTK_WIDGET_UNSET_FLAGS(vnc->mbutton, GTK_CAN_FOCUS); + + /* packing */ + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 1); + gtk_container_add(GTK_CONTAINER(vnc->win), vbox); + gtk_box_pack_start(GTK_BOX(vbox), vnc->draw, TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + + frame = gtk_frame_new(NULL); + gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(frame), vnc->line); + gtk_misc_set_alignment(GTK_MISC(vnc->line), 0, 0.5); + gtk_misc_set_padding(GTK_MISC(vnc->line), 3, 1); + + frame = gtk_frame_new(NULL); + gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(frame), vnc->res); + gtk_misc_set_padding(GTK_MISC(vnc->res), 3, 1); + + gtk_box_pack_start(GTK_BOX(hbox), vnc->mbutton, FALSE, TRUE, 0); + + /* show window */ + gtk_widget_show_all(vnc->win); + vnc_window_conf(vnc); + if (flags & VNC_FLAG_FULLSCREEN) + gtk_window_fullscreen(GTK_WINDOW(vnc->win)); + + /* opengl */ + if (0 == gl_init(vnc)) + vnc->have_gl = 1; + + /* rfb client */ + argv[1] = vnc->display; + fprintf(stderr, "%s: connecting to %s\n", __FUNCTION__, vnc->display); + if (8 == x11_red_bits) + vnc->client = rfbGetClient(8,3,4); + else + vnc->client = rfbGetClient(5,3,2); + if (NULL == vnc->client) + goto err; + rfbClientSetClientData(vnc->client, vnc_open, vnc); + vnc->client->MallocFrameBuffer = vnc_resize; + vnc->client->GotFrameBufferUpdate = vnc_update; + vnc->client->GetPassword = vnc_passwd; +#ifdef HAVE_VNC_TEXT + vnc->client->canHandleNewFBSize = TRUE; /* was added before textchat */ + vnc->client->HandleTextChat = vnc_textchat; +#endif + rfbClientLog = vnc_log; + rc = rfbInitClient(vnc->client, &argc, argv); + if (0 == rc) + goto err; + vnc->ch = g_io_channel_unix_new(vnc->client->sock); + vnc->id = g_io_add_watch(vnc->ch, G_IO_IN, vnc_data_cb, vnc); + vnc->connected = 1; + + return vnc->win; + + err: + vnc_release(vnc); + return NULL; +} + +#else /* HAVE_VNCCLIENT */ + +GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, + int debug_level) +{ + fprintf(stderr, "compiled without VNC support, sorry\n"); + return NULL; +} + +#endif /* HAVE_VNCCLIENT */ @@ -20,519 +20,48 @@ #include "x11.h" #include "vnc.h" -#ifdef HAVE_VNCCLIENT +#ifdef HAVE_GTK_VNC -#include <rfb/rfbclient.h> - -static int debug_libvnc; +#include <vncdisplay.h> /* ------------------------------------------------------------------ */ -struct pos { - int x; - int y; -}; - -struct rect { - int w; - int h; -}; - struct vnc_window { - /* vnc connection */ - char display[128]; - rfbClient *client; - GIOChannel *ch; - guint id, connected; - /* gtk */ GtkAccelGroup *ac; GtkActionGroup *ag; GtkUIManager *ui; GtkWidget *win; - GtkWidget *draw; + GtkWidget *vnc; GtkWidget *line, *res, *mbutton, *popup; - GdkCursor *on,*off; - int filter_installed; - int input_grabbed; - /* x11 */ - XImage *ximage; - void *shm; - GC gc; - Display *dpy; - unsigned char keydown[32]; - - /* opengl */ - int have_gl; - GLuint tex; - int tex_max; - unsigned char *tex_data; - unsigned int dirty_y1, dirty_y2; - - /* window / vnc display config */ - struct rect window; - struct pos vncoff; - struct rect vncdpy; - struct rect texture; - int updates, redraw; + /* connection */ + char display[100]; + char hostname[80]; + char tcpport[16]; + + /* state */ + int input_grabbed; + int connected; /* config */ int fullscreen; - int viewonly; int standalone; int showpointer; - int gl_allways; - int gl_fullscreen; - int uskbd; + int viewonly; int debug; }; /* ------------------------------------------------------------------ */ -/* data tables */ - -rfbKeySym linux_uskbd[][2] = { -#include "linux-uskbd.h" -}; -static int linux_uskbd_size = sizeof(linux_uskbd)/sizeof(linux_uskbd[0]); - -/* ------------------------------------------------------------------ */ -/* prototypes */ - -static GdkFilterReturn event_filter(GdkXEvent *gdkxevent, GdkEvent *gtkevent, - gpointer data); -static void vnc_window_conf(struct vnc_window *vnc); -static void vnc_window_texts(struct vnc_window *vnc); - -/* ------------------------------------------------------------------ */ -/* opengl bits */ - -static int gl_error; -static int gl_attrib[] = { GLX_RGBA, - GLX_RED_SIZE, 1, - GLX_GREEN_SIZE, 1, - GLX_BLUE_SIZE, 1, - GLX_DOUBLEBUFFER, - None }; - -static int -catch_gl_error(Display * dpy, XErrorEvent * event) -{ - fprintf(stderr,"WARNING: Your OpenGL setup is broken.\n"); - gl_error++; - return 0; -} - -static int gl_init(struct vnc_window *vnc) -{ - Window win = gdk_x11_drawable_get_xid(vnc->draw->window); - Screen *scr = DefaultScreenOfDisplay(vnc->dpy); - void *old_handler; - XVisualInfo *visinfo; - GLXContext ctx; - - if (vnc->debug) - fprintf(stderr, "gl: init [window=0x%lx]\n", win); - if (!win) - return -1; - visinfo = glXChooseVisual(vnc->dpy, XScreenNumberOfScreen(scr), - gl_attrib); - if (!visinfo) { - if (vnc->debug) - fprintf(stderr,"gl: can't get visual (rgb,db)\n"); - return -1; - } - ctx = glXCreateContext(vnc->dpy, visinfo, NULL, True); - if (!ctx) { - if (vnc->debug) - fprintf(stderr,"gl: can't create context\n"); - return -1; - } - - /* there is no point in using OpenGL for image scaling if it - * isn't hardware accelerated ... */ - if (vnc->debug) - fprintf(stderr, "gl: DRI=%s\n", - glXIsDirect(vnc->dpy, ctx) ? "Yes" : "No"); - if (!glXIsDirect(vnc->dpy, ctx)) - return -1; - - old_handler = XSetErrorHandler(catch_gl_error); - glXMakeCurrent(vnc->dpy, win, ctx); - XSync(vnc->dpy, False); - XSetErrorHandler(old_handler); - if (gl_error) - return -1; - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &vnc->tex_max); - if (vnc->debug) - fprintf(stderr, "gl: texture max size: %d\n", vnc->tex_max); - return 0; -} - -static void gl_resize_window(struct vnc_window *vnc) -{ - if (!vnc->tex) - return; - - glClearColor (0.0, 0.0, 0.0, 0.0); - glShadeModel(GL_FLAT); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glViewport(0, 0, vnc->window.w, vnc->window.h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, vnc->window.w, 0.0, vnc->window.h, -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - vnc->redraw++; -} - -static void gl_blit(struct vnc_window *vnc, int y1, int y2) -{ - Window win = gdk_x11_drawable_get_xid(vnc->draw->window); - float x,y; - unsigned int ww = vnc->window.w, wh = vnc->window.h; - int wx = 0, wy = 0; - - if (!vnc->tex) - return; - - if (y1 > vnc->vncdpy.h) - y1 = vnc->vncdpy.h; - if (y2 > vnc->vncdpy.h) - y2 = vnc->vncdpy.h; - - glBindTexture(GL_TEXTURE_2D, vnc->tex); - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0,y1, vnc->vncdpy.w, y2 - y1, - GL_BGRA_EXT /* GL_RGB */, - GL_UNSIGNED_BYTE, - vnc->tex_data + y1 * 4 * vnc->vncdpy.w); - x = (float)vnc->vncdpy.w / vnc->texture.w; - y = (float)vnc->vncdpy.h / vnc->texture.h; - - glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); - glBegin(GL_QUADS); - glTexCoord2f(0,y); glVertex3f(wx, wy, 0); - glTexCoord2f(0,0); glVertex3f(wx, wy+wh, 0); - glTexCoord2f(x,0); glVertex3f(wx+ww, wy+wh, 0); - glTexCoord2f(x,y); glVertex3f(wx+ww, wy, 0); - glEnd(); - glXSwapBuffers(vnc->dpy, win); - glDisable(GL_TEXTURE_2D); -} - -static void gl_update_vnc(struct vnc_window *vnc, - int x, int y, int w, int h) -{ - if (vnc->dirty_y1 > y) - vnc->dirty_y1 = y; - if (vnc->dirty_y2 < y+h) - vnc->dirty_y2 = y+h; - vnc->updates++; -} - -static void gl_update_win(struct vnc_window *vnc, - int x, int y, int w, int h) -{ - vnc->dirty_y1 = 0; - vnc->dirty_y2 = vnc->vncdpy.h; - vnc->updates++; -} - -static void gl_flush(struct vnc_window *vnc) -{ - if (vnc->redraw) { - vnc->dirty_y1 = 0; - vnc->dirty_y2 = vnc->vncdpy.h; - } - gl_blit(vnc, vnc->dirty_y1, vnc->dirty_y2); - - vnc->dirty_y1 = 99999; - vnc->dirty_y2 = 0; - vnc->updates = 0; - vnc->redraw = 0; -} - -/* ------------------------------------------------------------------ */ -/* x11 draw bits */ - -static void x11_update_win(struct vnc_window *vnc, - int x, int y, int w, int h) -{ - Window win = gdk_x11_drawable_get_xid(vnc->draw->window); - - if (!vnc->gc) { - XGCValues values; - XColor color, dummy; - Colormap cmap; - - cmap = gdk_x11_colormap_get_xcolormap(gtk_widget_get_colormap(vnc->win)); - XAllocNamedColor(vnc->dpy, cmap, "#404040", &color, &dummy); - values.function = GXcopy; - values.foreground = color.pixel; - vnc->gc = XCreateGC(vnc->dpy, win, - GCForeground|GCFunction, - &values); - } - - if (x < vnc->vncoff.x) - vnc->redraw++; - if (y < vnc->vncoff.y) - vnc->redraw++; - if (x+w > vnc->vncoff.x + vnc->vncdpy.w) - vnc->redraw++; - if (y+h > vnc->vncoff.y + vnc->vncdpy.h) - vnc->redraw++; - - if (vnc->redraw) { - XFillRectangle(vnc->dpy, win, vnc->gc, - 0,0, vnc->window.w, vnc->window.h); - XPUTIMAGE(vnc->dpy, win, vnc->gc, vnc->ximage, 0,0, - vnc->vncoff.x, vnc->vncoff.y, - vnc->vncdpy.w, vnc->vncdpy.h); - vnc->redraw = 0; - } else { - XPUTIMAGE(vnc->dpy, win, vnc->gc, vnc->ximage, - x-vnc->vncoff.x, y-vnc->vncoff.y, - x,y, w,h); - } -} - -static void x11_update_vnc(struct vnc_window *vnc, - int x, int y, int w, int h) -{ - x11_update_win(vnc, x + vnc->vncoff.x, y + vnc->vncoff.y, w, h); -} - -static void x11_resize_window(struct vnc_window *vnc) -{ - vnc->vncoff.x = (vnc->window.w - vnc->vncdpy.w) / 2; - vnc->vncoff.y = (vnc->window.h - vnc->vncdpy.h) / 2; -} - -static void x11_flush(struct vnc_window *vnc) -{ - if (vnc->redraw) - x11_update_win(vnc, 0, 0, vnc->vncdpy.w, vnc->vncdpy.w); - vnc->updates = 0; - vnc->redraw = 0; -} - -/* ------------------------------------------------------------------ */ -/* x11/gl wrappers */ - -/* vnc display coordinates */ -static void dpy_update_vnc(struct vnc_window *vnc, - int x, int y, int w, int h) -{ - if (vnc->debug) - fprintf(stderr, "%s: mode%s%s, %dx%d+%d+%d\n", __FUNCTION__, - vnc->tex ? " GL" : "", - vnc->ximage ? " X11" : "", - w, h, x, y); - if (vnc->ximage) - x11_update_vnc(vnc, x, y, w, h); - if (vnc->tex) - gl_update_vnc(vnc, x, y, w, h); -} - -/* app window coordinates */ -static void dpy_update_win(struct vnc_window *vnc, - int x, int y, int w, int h) -{ - if (vnc->debug) - fprintf(stderr, "%s: mode%s%s, %dx%d+%d+%d\n", __FUNCTION__, - vnc->tex ? " GL" : "", - vnc->ximage ? " X11" : "", - w, h, x, y); - if (vnc->ximage) - x11_update_win(vnc, x, y, w, h); - if (vnc->tex) - gl_update_win(vnc, x, y, w, h); -} - -static void dpy_redraw(struct vnc_window *vnc) -{ - vnc->redraw++; -} - -static void dpy_flush(struct vnc_window *vnc, const char *caller) -{ - if (vnc->debug) - fprintf(stderr, "%s: from %s, mode%s%s, updates %d, redraw %d\n", - __FUNCTION__, caller, - vnc->tex ? " GL" : "", - vnc->ximage ? " X11" : "", - vnc->updates, vnc->redraw); - if (!vnc->updates && !vnc->redraw) - return; - - if (vnc->ximage) - x11_flush(vnc); - if (vnc->tex) - gl_flush(vnc); -} - -static int dpy_gl_check(struct vnc_window *vnc) -{ - int using_gl = 0; - - if (vnc->have_gl && vnc->vncdpy.w < vnc->tex_max && vnc->vncdpy.h < vnc->tex_max) { - if (vnc->gl_allways) - using_gl = 1; - if (vnc->gl_fullscreen && vnc->fullscreen) - using_gl = 1; - } - return using_gl; -} - -static void dpy_setup(struct vnc_window *vnc, int width, int height, int using_gl) -{ - /* cleanup */ - if (vnc->ximage) { - x11_destroy_ximage(vnc->dpy, vnc->ximage, vnc->shm); - vnc->ximage = NULL; - } - if (vnc->tex) { - /* FIXME: release texture */ - vnc->tex = 0; - vnc->vncdpy.w = 0; - vnc->vncdpy.h = 0; - free(vnc->tex_data); - vnc->tex_data = NULL; - } - - vnc->vncdpy.w = width; - vnc->vncdpy.h = height; - - if (!using_gl) { - /* init X11 */ - vnc->ximage = x11_create_ximage(vnc->dpy, vnc->vncdpy.w, vnc->vncdpy.h, &vnc->shm); - if (NULL == vnc->ximage) { - fprintf(stderr, "Oops: creating ximage failed\n"); - return; - } - - vnc->client->width = - vnc->ximage->bytes_per_line / (vnc->ximage->bits_per_pixel / 8); - vnc->client->frameBuffer = (void*)vnc->ximage->data; - - vnc->client->format.bitsPerPixel = vnc->ximage->bits_per_pixel; - vnc->client->format.redShift = x11_red_shift; - vnc->client->format.greenShift = x11_green_shift; - vnc->client->format.blueShift = x11_blue_shift; - vnc->client->format.redMax = (1 << x11_red_bits) - 1; - vnc->client->format.greenMax = (1 << x11_green_bits) - 1; - vnc->client->format.blueMax = (1 << x11_blue_bits) - 1; - } else { - /* init OpenGL */ - void *dummy; - int i; - - /* figure texture size (power of two) */ - for (i = 0; vnc->vncdpy.w >= (1 << i); i++) - ; - vnc->texture.w = (1 << i); - for (i = 0; vnc->vncdpy.h >= (1 << i); i++) - ; - vnc->texture.h = (1 << i); - if (vnc->debug) - fprintf(stderr,"%s: client %dx%d, tex %dx%d\n", __FUNCTION__, - vnc->vncdpy.w, vnc->vncdpy.h, vnc->texture.w, vnc->texture.h); - - /* create texture */ - glGenTextures(1, &vnc->tex); - glBindTexture(GL_TEXTURE_2D, vnc->tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - dummy = malloc(vnc->texture.w * vnc->texture.h * 4); - memset(dummy, 128, vnc->texture.w * vnc->texture.h * 4); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, - vnc->texture.w, vnc->texture.h, 0, - GL_RGB, GL_UNSIGNED_BYTE, - dummy); - free(dummy); - - /* image buffer */ - vnc->tex_data = malloc(vnc->vncdpy.w * vnc->vncdpy.h * 4); - vnc->client->frameBuffer = vnc->tex_data; - - vnc->client->format.bitsPerPixel = 32; - vnc->client->format.redShift = 16; - vnc->client->format.greenShift = 8; - vnc->client->format.blueShift = 0; - vnc->client->format.redMax = 255; - vnc->client->format.greenMax = 255; - vnc->client->format.blueMax = 255; - } - - if (vnc->debug) - fprintf(stderr, "%s: SetFormatAndEncodings: %s\n", __FUNCTION__, - using_gl ? "GL" : "X11" ); - SetFormatAndEncodings(vnc->client); - SendFramebufferUpdateRequest(vnc->client, 0, 0, - vnc->vncdpy.w, vnc->vncdpy.h, False); - vnc_window_conf(vnc); -} - -static void dpy_resize_window(struct vnc_window *vnc) -{ - int using_gl; - - vnc->vncoff.x = 0; - vnc->vncoff.y = 0; - using_gl = dpy_gl_check(vnc); - if ((vnc->tex && !using_gl) || - (vnc->ximage && using_gl)) { - /* switching display mode */ - dpy_setup(vnc, vnc->client->width, vnc->client->height, using_gl); - } else { - if (vnc->ximage) - x11_resize_window(vnc); - if (vnc->tex) - gl_resize_window(vnc); - dpy_redraw(vnc); - } - vnc_window_texts(vnc); -} - -/* ------------------------------------------------------------------ */ /* helper functions */ -static void XAddInput(Display *dpy, Window win, long mask) -{ - XWindowAttributes attr; - - XGetWindowAttributes(dpy, win, &attr); - XSelectInput(dpy, win, attr.your_event_mask | mask); -} - -static GdkCursor* empty_cursor(void) -{ - static char bits[32]; - GdkCursor *cursor; - GdkPixmap *pixmap; - GdkColor fg = { 0, 0, 0, 0 }; - GdkColor bg = { 0, 0, 0, 0 }; - - pixmap = gdk_bitmap_create_from_data(NULL, bits, 16, 16); - cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &fg, &bg, 0, 0); - gdk_pixmap_unref(pixmap); - return cursor; -} - static void vnc_window_texts(struct vnc_window *vnc) { + const char *name = vnc_display_get_name(VNC_DISPLAY(vnc->vnc)); char textline[256]; - int pos; - if (vnc->client && vnc->client->desktopName && - strlen(vnc->client->desktopName)) { - gtk_window_set_title(GTK_WINDOW(vnc->win), vnc->client->desktopName); + if (vnc->connected) { + gtk_window_set_title(GTK_WINDOW(vnc->win), name); } else { snprintf(textline, sizeof(textline), "connecting to %s ...", vnc->display); @@ -542,151 +71,28 @@ static void vnc_window_texts(struct vnc_window *vnc) if (vnc->input_grabbed) { gtk_label_set_text(GTK_LABEL(vnc->line), "Press Ctrl-Alt to release input grab."); - } else if (vnc->client) { + } else if (vnc->connected) { snprintf(textline, sizeof(textline), "VNC: \"%s\" at %s", - vnc->client->desktopName ?: "", vnc->display); + name ?: "", vnc->display); gtk_label_set_text(GTK_LABEL(vnc->line), textline); + } else { + gtk_label_set_text(GTK_LABEL(vnc->line), "VNC: disconnected"); } - if (vnc->client) { - pos = 0; - pos += snprintf(textline+pos, sizeof(textline)-pos, "%dx%d", - vnc->vncdpy.w, vnc->vncdpy.h); - if (vnc->vncdpy.w != vnc->window.w || - vnc->vncdpy.h != vnc->window.h) - pos += snprintf(textline+pos, sizeof(textline)-pos, " @ %dx%d", - vnc->window.w, vnc->window.h); - pos += snprintf(textline+pos, sizeof(textline)-pos, " (%s)", - vnc->ximage ? "X11" : "GL"); - gtk_label_set_text(GTK_LABEL(vnc->res), textline); - } -} - -static void vnc_window_conf(struct vnc_window *vnc) -{ - if (!vnc->draw) - return; - if (vnc->client) - gtk_widget_set_size_request(vnc->draw, vnc->vncdpy.w, vnc->vncdpy.h); - if (vnc->draw->window) { - gdk_window_set_cursor(vnc->draw->window, - vnc->showpointer ? vnc->on : vnc->off); - /* FIXME */ - XAddInput(vnc->dpy, gdk_x11_drawable_get_xid(vnc->draw->window), - KeymapStateMask); - } - dpy_resize_window(vnc); + snprintf(textline, sizeof(textline), "%dx%d", + vnc_display_get_width(VNC_DISPLAY(vnc->vnc)), + vnc_display_get_height(VNC_DISPLAY(vnc->vnc))); + gtk_label_set_text(GTK_LABEL(vnc->res), textline); } static void vnc_release(struct vnc_window *vnc) { - int sock = -1; - if (NULL == vnc) return; - if (vnc->connected && vnc->client) { - /* - * library bugs? - * - calling rfbClientCleanup() unconnected segfaults - * - rfbClientCleanup() doesn't close the socket - */ - sock = vnc->client->sock; - rfbClientCleanup(vnc->client); - vnc->client = NULL; - } - if (vnc->filter_installed) - gdk_window_remove_filter(vnc->draw->window, event_filter, vnc); - if (vnc->id) - g_source_destroy(g_main_context_find_source_by_id - (g_main_context_default(), vnc->id)); - if (vnc->ximage) - x11_destroy_ximage(vnc->dpy, vnc->ximage, vnc->shm); - if (vnc->gc) - XFreeGC(vnc->dpy, vnc->gc); - if (-1 != sock) - close(sock); free(vnc); } -static void grab_input(struct vnc_window *vnc, guint32 time) -{ - if (vnc->viewonly) - return; - if (vnc->input_grabbed) - return; - gdk_pointer_grab(vnc->draw->window, - FALSE, - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK, - vnc->draw->window, - NULL, - time); - gdk_keyboard_grab(vnc->draw->window, - FALSE, - time); - vnc->input_grabbed = 1; - vnc_window_texts(vnc); -} - -static void ungrab_input(struct vnc_window *vnc, guint32 time) -{ - if (!vnc->input_grabbed) - return; - gdk_pointer_ungrab(time); - gdk_keyboard_ungrab(time); - vnc->input_grabbed = 0; - vnc_window_texts(vnc); -} - -/* ------------------------------------------------------------------ */ -/* libvncclient callbacks */ - -static rfbBool vnc_resize(rfbClient* client) -{ - struct vnc_window *vnc = rfbClientGetClientData(client, vnc_open); - - if (vnc->debug) - fprintf(stderr, "%s: %dx%d\n", - __FUNCTION__, client->width, client->height); - if (vnc->vncdpy.w == client->width && - vnc->vncdpy.h == client->height) { - if (vnc->debug) - fprintf(stderr, "%s: no size change, early exit\n", __FUNCTION__); - return TRUE; - } - - dpy_setup(vnc, client->width, client->height, dpy_gl_check(vnc)); - return TRUE; -} - -static void vnc_update(rfbClient* cl, int x, int y, int w, int h) -{ - struct vnc_window *vnc = rfbClientGetClientData(cl, vnc_open); - - if (!GTK_WIDGET_DRAWABLE(vnc->draw)) - return; - dpy_update_vnc(vnc, x,y, w,h); -} - -#ifdef HAVE_VNC_TEXT -static void vnc_textchat(rfbClient* cl, int value, char *text) -{ - switch(value) { - case rfbTextChatOpen: - fprintf(stderr,"%s: Open\n", __FUNCTION__); - break; - case rfbTextChatClose: - case rfbTextChatFinished: - fprintf(stderr,"%s: Close/Finished\n", __FUNCTION__); - break; - default: - fprintf(stderr,"%s: \"%s\"\n", __FUNCTION__, text); - break; - } -} -#endif - +#if 0 static char *vnc_passwd(rfbClient* cl) { struct vnc_window *vnc = rfbClientGetClientData(cl, vnc_open); @@ -740,238 +146,94 @@ static char *vnc_passwd(rfbClient* cl) return passwd; } - -static void vnc_log(const char *format, ...) -{ - va_list args; - - if (!debug_libvnc) - return; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); -} +#endif /* ------------------------------------------------------------------ */ -/* glib/gtk callbacks */ +/* vnc widget callbacks */ -static gboolean vnc_data_cb(GIOChannel *source, GIOCondition condition, - gpointer data) +static void vnc_connected(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; - if (!HandleRFBServerMessage(vnc->client)) { - /* server closed connection */ - g_source_destroy(g_main_context_find_source_by_id - (g_main_context_default(), vnc->id)); - vnc->id = 0; - gtk_widget_destroy(vnc->win); - } - dpy_flush(vnc, __FUNCTION__); - return TRUE; + if (vnc->debug) + fprintf(stderr, "%s\n", __FUNCTION__); } -static void destroy_cb(GtkWidget *widget, gpointer data) +static void vnc_initialized(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; if (vnc->debug) - fprintf(stderr,"%s: called\n", __FUNCTION__); - if (vnc->standalone) - gtk_main_quit(); - vnc_release(vnc); + fprintf(stderr, "%s\n", __FUNCTION__); + vnc->connected = 1; + vnc_window_texts(vnc); } -static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data) +static void vnc_disconnected(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; - dpy_update_win(vnc, event->area.x, event->area.y, - event->area.width, event->area.height); - if (0 == event->count) - dpy_flush(vnc, __FUNCTION__); - return TRUE; + if (vnc->debug) + fprintf(stderr, "%s\n", __FUNCTION__); + vnc->connected = 0; + vnc_window_texts(vnc); } -static gboolean configure_cb(GtkWidget *widget, GdkEventConfigure *event, - gpointer data) +static void vnc_grab(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; if (vnc->debug) - fprintf(stderr,"%s: %dx%d\n", __FUNCTION__, - event->width, event->height); - vnc->window.w = event->width; - vnc->window.h = event->height; - dpy_resize_window(vnc); - dpy_flush(vnc, __FUNCTION__); - return TRUE; + fprintf(stderr, "%s\n", __FUNCTION__); + vnc->input_grabbed = 1; + vnc_window_texts(vnc); } -static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, - gpointer data) +static void vnc_ungrab(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; - GtkWidget *item; - if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { - vnc->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; - if (vnc->debug) - fprintf(stderr, "%s: fullscreen %s\n", __FUNCTION__, - vnc->fullscreen ? "on" : "off"); - item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/FullScreen"); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->fullscreen); - } - return TRUE; + if (vnc->debug) + fprintf(stderr, "%s\n", __FUNCTION__); + vnc->input_grabbed = 0; + vnc_window_texts(vnc); } -static void send_mouse(struct vnc_window *vnc, int x, int y, - int x11state, int x11press, int x11release) +static void vnc_credential(GtkWidget *vncdisplay, void *data) { - static const struct { - int rfbmask; - int x11mask; - int x11nr; - } buttons[] = { - { - .rfbmask = rfbButton1Mask, - .x11mask = Button1Mask, - .x11nr = Button1, - },{ - .rfbmask = rfbButton2Mask, - .x11mask = Button2Mask, - .x11nr = Button2, - },{ - .rfbmask = rfbButton3Mask, - .x11mask = Button3Mask, - .x11nr = Button3, - },{ - .rfbmask = rfbButton4Mask, - .x11mask = Button4Mask, - .x11nr = Button4, - },{ - .rfbmask = rfbButton5Mask, - .x11mask = Button5Mask, - .x11nr = Button5, - } - }; - int i, rfbstate = 0; - - if (vnc->viewonly) - return; - - for (i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++) { - if (x11state & buttons[i].x11mask) - rfbstate |= buttons[i].rfbmask; - if (x11press == buttons[i].x11nr) - rfbstate |= buttons[i].rfbmask; - if (x11release == buttons[i].x11nr) - rfbstate &= ~buttons[i].rfbmask; - } - - /* fixup mouse coordinates */ - x -= vnc->vncoff.x; - y -= vnc->vncoff.y; - if (vnc->tex) { - x = x * vnc->window.w / vnc->vncdpy.w; - y = y * vnc->window.h / vnc->vncdpy.h; - } - if (x < 0) - x = 0; - if (y < 0) - y = 0; - if (x >= vnc->vncdpy.w) - x = vnc->vncdpy.w -1; - if (y >= vnc->vncdpy.h) - y = vnc->vncdpy.h -1; +// struct vnc_window *vnc = data; - if (vnc->debug) - fprintf(stderr,"%s: +%d+%d x11state 0x%x rfbstate 0x%x\n", - __FUNCTION__, x, y, x11state, rfbstate); - SendPointerEvent(vnc->client, x, y, rfbstate); + fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__); } -static gboolean button_cb(GtkWidget *widget, GdkEventButton *event, - gpointer data) -{ - struct vnc_window *vnc = data; - switch (event->type) { - case GDK_BUTTON_PRESS: - send_mouse(vnc, event->x, event->y, event->state, event->button, 0); - grab_input(vnc, event->time); - break; - case GDK_BUTTON_RELEASE: - send_mouse(vnc, event->x, event->y, event->state, 0, event->button); - break; - default: - /* keep gcc happy */ - break; - } - return TRUE; -} +/* ------------------------------------------------------------------ */ +/* glib/gtk callbacks */ -static gboolean motion_cb(GtkWidget *widget, GdkEventMotion *event, - gpointer data) +static void destroy_cb(GtkWidget *widget, gpointer data) { struct vnc_window *vnc = data; - send_mouse(vnc, event->x, event->y, event->state, 0, 0); - return TRUE; -} - -static void key_local(struct vnc_window *vnc, GdkEventKey *event) -{ - int keydown; if (vnc->debug) - fprintf(stderr,"%s[%d]: called: keysym %d\n", __FUNCTION__, - event->type, event->keyval); - keydown = (8 == event->type); - if (!vnc->viewonly) - SendKeyEvent(vnc->client, event->keyval, keydown ? TRUE : FALSE); -} - -static void key_uskbd(struct vnc_window *vnc, GdkEventKey *event) -{ - rfbKeySym keysym = 0; - int shift,keydown; - - keydown = (8 == event->type); - if (event->hardware_keycode < 256) { - int by = event->hardware_keycode / 8; - int bi = event->hardware_keycode % 8; - if (keydown) - vnc->keydown[by] |= (1 << bi); - else - vnc->keydown[by] &= ~(1 << bi); - } - - shift = (event->state & GDK_SHIFT_MASK) ? 1 : 0; - if (event->hardware_keycode < linux_uskbd_size) { - keysym = linux_uskbd[event->hardware_keycode][shift]; - if (0 == keysym) - keysym = linux_uskbd[event->hardware_keycode][0]; - } - - if (vnc->debug || 0 == keysym) - fprintf(stderr,"%s[%d]: called: keycode %d => keysym %d\n", __FUNCTION__, - event->type, event->hardware_keycode, keysym); - if (keysym && !vnc->viewonly) - SendKeyEvent(vnc->client, keysym, keydown ? TRUE : FALSE); + fprintf(stderr,"%s: called\n", __FUNCTION__); + if (vnc->standalone) + gtk_main_quit(); + vnc_release(vnc); } -static gboolean key_cb(GtkWidget *widget, GdkEventKey *event, - gpointer data) +static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, + gpointer data) { struct vnc_window *vnc = data; - int mask = GDK_CONTROL_MASK | GDK_MOD1_MASK; - - if (mask == (event->state & mask)) - ungrab_input(vnc, event->time); + GtkWidget *item; - if (vnc->uskbd) - key_uskbd(vnc, event); - else - key_local(vnc, event); + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { + vnc->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; + if (vnc->debug) + fprintf(stderr, "%s: fullscreen %s\n", __FUNCTION__, + vnc->fullscreen ? "on" : "off"); + item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/FullScreen"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->fullscreen); + } return TRUE; } @@ -983,46 +245,6 @@ static void menu_btn(GtkWidget *widget, gpointer data) 0, gtk_get_current_event_time()); } -static GdkFilterReturn event_filter(GdkXEvent *gdkxevent, GdkEvent *gtkevent, - gpointer data) -{ - struct vnc_window *vnc = data; - XEvent *xevent = gdkxevent; - int by, bi, keydown, keycode; - rfbKeySym keysym = 0; - - switch (xevent->type) { - case KeymapNotify: - if (!vnc->uskbd) - return GDK_FILTER_REMOVE; - if (!GTK_WIDGET_HAS_FOCUS(vnc->draw)) - return GDK_FILTER_REMOVE; - for (by = 0; by < 32; by++) { - if (vnc->keydown[by] == xevent->xkeymap.key_vector[by]) - continue; - for (bi = 0; bi < 8; bi++) { - if ((vnc->keydown[by] & (1 << bi)) == - (xevent->xkeymap.key_vector[by] & (1 << bi))) - continue; - keydown = xevent->xkeymap.key_vector[by] & (1 << bi); - keycode = by * 8 + bi; - keysym = linux_uskbd[keycode][0]; - if (!keysym) - continue; - if (vnc->debug) - fprintf(stderr,"%s: KeymapNotify: %-7s %3d\n", __FUNCTION__, - keydown ? "press" : "release", keycode); - if (!vnc->viewonly) - SendKeyEvent(vnc->client, keysym, keydown ? TRUE : FALSE); - } - } - memcpy(vnc->keydown, xevent->xkeymap.key_vector, 32); - return GDK_FILTER_REMOVE; - default: - return GDK_FILTER_CONTINUE; - } -} - /* ------------------------------------------------------------------ */ static void menu_cb_full_screen(GtkToggleAction *action, gpointer user_data) @@ -1036,21 +258,16 @@ static void menu_cb_full_screen(GtkToggleAction *action, gpointer user_data) gtk_window_unfullscreen(GTK_WINDOW(vnc->win)); } -static void menu_cb_us_keyboard(GtkToggleAction *action, gpointer user_data) -{ - struct vnc_window *vnc = user_data; - - vnc->uskbd = gtk_toggle_action_get_active(action); -} - static void menu_cb_show_pointer(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; vnc->showpointer = gtk_toggle_action_get_active(action); +#if 0 if (vnc->draw->window) gdk_window_set_cursor(vnc->draw->window, vnc->showpointer ? vnc->on : vnc->off); +#endif } static void menu_cb_view_only(GtkToggleAction *action, gpointer user_data) @@ -1060,29 +277,6 @@ static void menu_cb_view_only(GtkToggleAction *action, gpointer user_data) vnc->viewonly = gtk_toggle_action_get_active(action); } -static void menu_cb_gl(GtkRadioAction *action, GtkRadioAction *current, - gpointer user_data) -{ - struct vnc_window *vnc = user_data; - int value = gtk_radio_action_get_current_value(action); - - switch (value) { - case 1: /* OFF */ - vnc->gl_fullscreen = 0; - vnc->gl_allways = 0; - break; - case 2: /* fullscreen ON */ - vnc->gl_fullscreen = 1; - vnc->gl_allways = 0; - break; - case 3: /* allways ON */ - vnc->gl_fullscreen = 1; - vnc->gl_allways = 1; - break; - } - dpy_resize_window(vnc); -} - static void menu_cb_about(GtkMenuItem *item, void *user_data) { static char *comments = "simple vnc client"; @@ -1136,10 +330,6 @@ static const GtkToggleActionEntry tentries[] = { .accelerator = "F11", .callback = G_CALLBACK(menu_cb_full_screen), },{ - .name = "USKbd", - .label = "US _Keyboard", - .callback = G_CALLBACK(menu_cb_us_keyboard), - },{ .name = "ShowPointer", .label = "Show _Pointer", .callback = G_CALLBACK(menu_cb_show_pointer), @@ -1150,34 +340,13 @@ static const GtkToggleActionEntry tentries[] = { } }; -static const GtkRadioActionEntry rentries[] = { - { - .name = "GL_OFF", - .label = "_Disable OpenGL scaling", - .value = 1, - },{ - .name = "GL_FSonly", - .label = "OpenGL scaling for fullscreen", - .value = 2, - },{ - .name = "GL_ON", - .label = "Allways scale using _OpenGL", - .value = 3, - } -}; - static char ui_xml[] = "<ui>" " <popup action='ConfMenu'>" " <menuitem action='FullScreen'/>" -" <menuitem action='USKbd'/>" " <menuitem action='ShowPointer'/>" " <menuitem action='ViewOnly'/>" " <separator/>" -" <menuitem action='GL_OFF'/>" -" <menuitem action='GL_FSonly'/>" -" <menuitem action='GL_ON'/>" -" <separator/>" " <menuitem action='About'/>" " <menuitem action='Close'/>" " </popup>" @@ -1190,12 +359,8 @@ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, int debug_level) { GtkWidget *vbox, *hbox, *frame, *item; - GtkAction *action; GError *err; - char *argv[] = { "vnc-client", NULL, NULL }; - int argc = sizeof(argv)/sizeof(argv[0]) -1; struct vnc_window *vnc; - int rc; vnc = malloc(sizeof(*vnc)); if (NULL == vnc) @@ -1203,61 +368,38 @@ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, memset(vnc,0,sizeof(*vnc)); vnc->standalone = (flags & VNC_FLAG_STANDALONE); vnc->showpointer = (flags & VNC_FLAG_SHOW_MOUSE); - vnc->uskbd = (flags & VNC_FLAG_US_KBD); vnc->viewonly = (flags & VNC_FLAG_VIEW_ONLY); - vnc->gl_allways = (flags & VNC_FLAG_GL_ALLWAYS); - vnc->gl_fullscreen = (flags & VNC_FLAG_GL_FULLSCR); vnc->debug = debug_level; - debug_libvnc = debug_level; - snprintf(vnc->display, sizeof(vnc->display), - "%s:%d", hostname, tcpport - 5900); - vnc->dirty_y1 = 99999; - vnc->dirty_y2 = 0; - - /* x11 */ - vnc->dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); - if (NULL == x11_info) - if (0 != x11_color_init(vnc->dpy)) - goto err; + snprintf(vnc->display, sizeof(vnc->display), "%s:%d", + hostname, tcpport - 5900); + snprintf(vnc->hostname, sizeof(vnc->hostname),"%s", hostname); + snprintf(vnc->tcpport, sizeof(vnc->tcpport),"%d", tcpport); /* gtk toplevel */ vnc->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(G_OBJECT(vnc->win), "destroy", G_CALLBACK(destroy_cb), vnc); - vnc->on = gdk_cursor_new(GDK_LEFT_PTR); - vnc->off = empty_cursor(); - - /* gtk drawing area */ - vnc->draw = gtk_drawing_area_new(); - GTK_WIDGET_SET_FLAGS(vnc->draw, GTK_CAN_FOCUS); - gtk_widget_add_events(vnc->draw, - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_KEY_PRESS_MASK | - GDK_KEY_RELEASE_MASK | - GDK_EXPOSURE_MASK); - gtk_widget_set_app_paintable(vnc->draw, TRUE); - gtk_widget_set_double_buffered(vnc->draw, FALSE); - g_signal_connect(G_OBJECT(vnc->draw), "expose-event", - G_CALLBACK(expose_cb), vnc); - g_signal_connect(G_OBJECT(vnc->draw), "button-press-event", - G_CALLBACK(button_cb), vnc); - g_signal_connect(G_OBJECT(vnc->draw), "button-release-event", - G_CALLBACK(button_cb), vnc); - g_signal_connect(G_OBJECT(vnc->draw), "motion-notify-event", - G_CALLBACK(motion_cb), vnc); - g_signal_connect(G_OBJECT(vnc->draw), "key-press-event", - G_CALLBACK(key_cb), vnc); - g_signal_connect(G_OBJECT(vnc->draw), "key-release-event", - G_CALLBACK(key_cb), vnc); - g_signal_connect(G_OBJECT(vnc->draw), "configure-event", - G_CALLBACK(configure_cb), vnc); g_signal_connect(G_OBJECT(vnc->win), "window-state-event", G_CALLBACK(window_state_cb), vnc); - gdk_window_add_filter(NULL, event_filter, vnc); - vnc->filter_installed = 1; + + /* vnc display widget */ + vnc->vnc = vnc_display_new(); + gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-connected", + GTK_SIGNAL_FUNC(vnc_connected), vnc); + gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-initialized", + GTK_SIGNAL_FUNC(vnc_initialized), vnc); + gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-disconnected", + GTK_SIGNAL_FUNC(vnc_disconnected), vnc); + gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-pointer-grab", + GTK_SIGNAL_FUNC(vnc_grab), vnc); + gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-pointer-ungrab", + GTK_SIGNAL_FUNC(vnc_ungrab), vnc); + gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-auth-credential", + GTK_SIGNAL_FUNC(vnc_credential), NULL); + vnc_display_set_pointer_grab(VNC_DISPLAY(vnc->vnc), TRUE); +// vnc_display_set_keyboard_grab(VNC_DISPLAY(vnc->vnc), TRUE); +// vnc_display_set_pointer_local(VNC_DISPLAY(vnc->vnc), TRUE); /* popup menu */ vnc->ui = gtk_ui_manager_new(); @@ -1265,9 +407,6 @@ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, gtk_action_group_add_actions(vnc->ag, entries, G_N_ELEMENTS(entries), vnc); gtk_action_group_add_toggle_actions(vnc->ag, tentries, G_N_ELEMENTS(tentries), vnc); - gtk_action_group_add_radio_actions(vnc->ag, rentries, - G_N_ELEMENTS(rentries), - 0, G_CALLBACK(menu_cb_gl), vnc); gtk_ui_manager_insert_action_group(vnc->ui, vnc->ag, 0); vnc->ac = gtk_ui_manager_get_accel_group(vnc->ui); gtk_window_add_accel_group(GTK_WINDOW(vnc->win), vnc->ac); @@ -1279,28 +418,18 @@ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, exit(1); } vnc->popup = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu"); - gtk_menu_set_title(GTK_MENU(vnc->popup), "Config"); + gtk_menu_set_title(GTK_MENU(vnc->popup), "Menu"); /* popup menu: initial state */ - item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/USKbd"); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->uskbd); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/ShowPointer"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->showpointer); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/ViewOnly"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->viewonly); - - action = gtk_ui_manager_get_action(vnc->ui, "/ConfMenu/GL_ON"); - if (vnc->gl_allways) - gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 3); - else if (vnc->gl_fullscreen) - gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 2); - else - gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 1); /* labels for the status line */ vnc->line = gtk_label_new("status line"); vnc->res = gtk_label_new("vnc screen resolution"); - vnc->mbutton = gtk_button_new_with_label("config"); + vnc->mbutton = gtk_button_new_with_label("menu"); g_signal_connect(G_OBJECT(vnc->mbutton), "clicked", G_CALLBACK(menu_btn), vnc); GTK_WIDGET_UNSET_FLAGS(vnc->mbutton, GTK_CAN_FOCUS); @@ -1309,7 +438,7 @@ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, vbox = gtk_vbox_new(FALSE, 0); hbox = gtk_hbox_new(FALSE, 1); gtk_container_add(GTK_CONTAINER(vnc->win), vbox); - gtk_box_pack_start(GTK_BOX(vbox), vnc->draw, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), vnc->vnc, TRUE, TRUE, 0); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); frame = gtk_frame_new(NULL); @@ -1327,38 +456,12 @@ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, /* show window */ gtk_widget_show_all(vnc->win); - vnc_window_conf(vnc); + vnc_window_texts(vnc); if (flags & VNC_FLAG_FULLSCREEN) gtk_window_fullscreen(GTK_WINDOW(vnc->win)); - /* opengl */ - if (0 == gl_init(vnc)) - vnc->have_gl = 1; - - /* rfb client */ - argv[1] = vnc->display; - fprintf(stderr, "%s: connecting to %s\n", __FUNCTION__, vnc->display); - if (8 == x11_red_bits) - vnc->client = rfbGetClient(8,3,4); - else - vnc->client = rfbGetClient(5,3,2); - if (NULL == vnc->client) - goto err; - rfbClientSetClientData(vnc->client, vnc_open, vnc); - vnc->client->MallocFrameBuffer = vnc_resize; - vnc->client->GotFrameBufferUpdate = vnc_update; - vnc->client->GetPassword = vnc_passwd; -#ifdef HAVE_VNC_TEXT - vnc->client->canHandleNewFBSize = TRUE; /* was added before textchat */ - vnc->client->HandleTextChat = vnc_textchat; -#endif - rfbClientLog = vnc_log; - rc = rfbInitClient(vnc->client, &argc, argv); - if (0 == rc) - goto err; - vnc->ch = g_io_channel_unix_new(vnc->client->sock); - vnc->id = g_io_add_watch(vnc->ch, G_IO_IN, vnc_data_cb, vnc); - vnc->connected = 1; + /* connect */ + vnc_display_open_host(VNC_DISPLAY(vnc->vnc), vnc->hostname, vnc->tcpport); return vnc->win; @@ -1367,7 +470,7 @@ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, return NULL; } -#else /* HAVE_VNCCLIENT */ +#else /* HAVE_GTK_VNC */ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, int debug_level) |