aboutsummaryrefslogtreecommitdiffstats
path: root/vnc-old.c
diff options
context:
space:
mode:
Diffstat (limited to 'vnc-old.c')
-rw-r--r--vnc-old.c1379
1 files changed, 1379 insertions, 0 deletions
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 */