diff options
author | Mateus Cesar Groess <mateuscg@gmail.com> | 2012-02-11 19:34:22 +0100 |
---|---|---|
committer | Christian Beier <dontmind@freeshell.org> | 2012-02-11 19:34:22 +0100 |
commit | 1078e8a8b050b5b4ebbcb011750f5dd2d8eacc37 (patch) | |
tree | c77de6a7641e39324743ca4c26fb402a52626912 /client_examples | |
parent | 4ed29e0a36e043ddf53f572b84004c7fe4d40b1d (diff) | |
download | libtdevnc-1078e8a8b050b5b4ebbcb011750f5dd2d8eacc37.tar.gz libtdevnc-1078e8a8b050b5b4ebbcb011750f5dd2d8eacc37.zip |
Here is a port of SDLvncviewer to GTK+2.
I think it may encourage people to implement more features for the viewer,
because a GTK GUI seems to be easier to implement than a SDL one
(and it is more integrated with the major Linux Desktops out there).
Signed-off-by: Christian Beier <dontmind@freeshell.org>
Diffstat (limited to 'client_examples')
-rw-r--r-- | client_examples/Makefile.am | 10 | ||||
-rw-r--r-- | client_examples/gtkvncviewer.c | 668 |
2 files changed, 677 insertions, 1 deletions
diff --git a/client_examples/Makefile.am b/client_examples/Makefile.am index 8619dc2..2779d53 100644 --- a/client_examples/Makefile.am +++ b/client_examples/Makefile.am @@ -28,7 +28,15 @@ endif SDLvncviewer_LDADD=$(LDADD) $(SDL_LIBS) $(X11_LIB) endif -noinst_PROGRAMS=ppmtest $(SDLVIEWER) $(FFMPEG_CLIENT) backchannel +if HAVE_LIBGTK +GTKVIEWER=gtkvncviewer +gtkvncviewer_SOURCES=gtkvncviewer.c +gtkvncviewer_CFLAGS=$(GTK_CFLAGS) +gtkvncviewer_LDADD=$(LDADD) $(GTK_LIBS) +endif + + +noinst_PROGRAMS=ppmtest $(SDLVIEWER) $(GTKVIEWER) $(FFMPEG_CLIENT) backchannel diff --git a/client_examples/gtkvncviewer.c b/client_examples/gtkvncviewer.c new file mode 100644 index 0000000..9e9e72b --- /dev/null +++ b/client_examples/gtkvncviewer.c @@ -0,0 +1,668 @@ + +/* + * Copyright (C) 2007 - Mateus Cesar Groess + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <rfb/rfbclient.h> + +static rfbClient *cl; +static gchar *server_cut_text = NULL; +static gboolean framebuffer_allocated = FALSE; + +/* Redraw the screen from the backing pixmap */ +static gboolean expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + static GdkImage *image = NULL; + + if (framebuffer_allocated == FALSE) { + + rfbClientSetClientData (cl, gtk_init, widget); + + image = gdk_drawable_get_image (widget->window, 0, 0, + widget->allocation.width, + widget->allocation.height); + + cl->frameBuffer= image->mem; + + cl->width = widget->allocation.width; + cl->height = widget->allocation.height; + + cl->format.bitsPerPixel = image->bits_per_pixel; + cl->format.redShift = image->visual->red_shift; + cl->format.greenShift = image->visual->green_shift; + cl->format.blueShift = image->visual->blue_shift; + + cl->format.redMax = (1 << image->visual->red_prec) - 1; + cl->format.greenMax = (1 << image->visual->green_prec) - 1; + cl->format.blueMax = (1 << image->visual->blue_prec) - 1; + + SetFormatAndEncodings (cl); + + framebuffer_allocated = TRUE; + } + + gdk_draw_image (GDK_DRAWABLE (widget->window), + widget->style->fg_gc[gtk_widget_get_state(widget)], + image, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return FALSE; +} + +struct { int gdk; int rfb; } buttonMapping[] = { + { GDK_BUTTON1_MASK, rfbButton1Mask }, + { GDK_BUTTON2_MASK, rfbButton2Mask }, + { GDK_BUTTON3_MASK, rfbButton3Mask }, + { 0, 0 } +}; + +static gboolean button_event (GtkWidget *widget, + GdkEventButton *event) +{ + int x, y; + GdkModifierType state; + int i, buttonMask; + + gdk_window_get_pointer (event->window, &x, &y, &state); + + for (buttonMask = 0, i = 0; buttonMapping[i].gdk; i++) + if (state & buttonMapping[i].gdk) + buttonMask |= buttonMapping[i].rfb; + SendPointerEvent (cl, x, y, buttonMask); + + return TRUE; +} + +static gboolean motion_notify_event (GtkWidget *widget, + GdkEventMotion *event) +{ + int x, y; + GdkModifierType state; + int i, buttonMask; + + if (event->is_hint) + gdk_window_get_pointer (event->window, &x, &y, &state); + else { + x = event->x; + y = event->y; + state = event->state; + } + + for (buttonMask = 0, i = 0; buttonMapping[i].gdk; i++) + if (state & buttonMapping[i].gdk) + buttonMask |= buttonMapping[i].rfb; + SendPointerEvent (cl, x, y, buttonMask); + + return TRUE; +} + +static void got_cut_text (rfbClient *cl, const char *text, int textlen) +{ + if (server_cut_text != NULL) { + g_free (server_cut_text); + server_cut_text = NULL; + } + + server_cut_text = g_strdup (text); +} + +void received_text_from_clipboard (GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + if (text) + SendClientCutText (cl, (char *) text, strlen (text)); +} + +static void clipboard_local_to_remote (GtkMenuItem *menuitem, + gpointer user_data) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (menuitem), + GDK_SELECTION_CLIPBOARD); + gtk_clipboard_request_text (clipboard, received_text_from_clipboard, + NULL); +} + +static void clipboard_remote_to_local (GtkMenuItem *menuitem, + gpointer user_data) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (menuitem), + GDK_SELECTION_CLIPBOARD); + + gtk_clipboard_set_text (clipboard, server_cut_text, + strlen (server_cut_text)); +} + +static void request_screen_refresh (GtkMenuItem *menuitem, + gpointer user_data) +{ + SendFramebufferUpdateRequest (cl, 0, 0, cl->width, cl->height, FALSE); +} + +static void send_f8 (GtkMenuItem *menuitem, + gpointer user_data) +{ + SendKeyEvent(cl, XK_F8, TRUE); + SendKeyEvent(cl, XK_F8, FALSE); +} + +static void send_crtl_alt_del (GtkMenuItem *menuitem, + gpointer user_data) +{ + SendKeyEvent(cl, XK_Control_L, TRUE); + SendKeyEvent(cl, XK_Alt_L, TRUE); + SendKeyEvent(cl, XK_Delete, TRUE); + SendKeyEvent(cl, XK_Alt_L, FALSE); + SendKeyEvent(cl, XK_Control_L, FALSE); + SendKeyEvent(cl, XK_Delete, FALSE); +} + +GtkWidget *dialog_connecting = NULL; + +static void show_connect_window(int argc, char **argv) +{ + GtkWidget *label; + char buf[256]; + + dialog_connecting = gtk_dialog_new_with_buttons ("VNC Viewer", + NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + /*GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL,*/ + NULL); + + /* FIXME: this works only when address[:port] is at end of arg list */ + char *server; + if(argc==1) + server = "localhost"; + else + server = argv[argc-1]; + snprintf(buf, 255, "Connecting to %s...", server); + + label = gtk_label_new (buf); + gtk_widget_show (label); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog_connecting)->vbox), + label); + + gtk_widget_show (dialog_connecting); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + +static void show_popup_menu() +{ + GtkWidget *popup_menu; + GtkWidget *menu_item; + + popup_menu = gtk_menu_new (); + + menu_item = gtk_menu_item_new_with_label ("Dismiss popup"); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item); + + menu_item = gtk_menu_item_new_with_label ("Clipboard: local -> remote"); + g_signal_connect (G_OBJECT (menu_item), "activate", + G_CALLBACK (clipboard_local_to_remote), NULL); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item); + + menu_item = gtk_menu_item_new_with_label ("Clipboard: local <- remote"); + g_signal_connect (G_OBJECT (menu_item), "activate", + G_CALLBACK (clipboard_remote_to_local), NULL); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item); + + menu_item = gtk_menu_item_new_with_label ("Request refresh"); + g_signal_connect (G_OBJECT (menu_item), "activate", + G_CALLBACK (request_screen_refresh), NULL); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item); + + menu_item = gtk_menu_item_new_with_label ("Send ctrl-alt-del"); + g_signal_connect (G_OBJECT (menu_item), "activate", + G_CALLBACK (send_crtl_alt_del), NULL); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item); + + menu_item = gtk_menu_item_new_with_label ("Send F8"); + g_signal_connect (G_OBJECT (menu_item), "activate", + G_CALLBACK (send_f8), NULL); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item); + + gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL, NULL, NULL, 0, + gtk_get_current_event_time()); +} + +static rfbKeySym gdkKey2rfbKeySym(guint keyval) +{ + rfbKeySym k = 0; + switch(keyval) { + case GDK_BackSpace: k = XK_BackSpace; break; + case GDK_Tab: k = XK_Tab; break; + case GDK_Clear: k = XK_Clear; break; + case GDK_Return: k = XK_Return; break; + case GDK_Pause: k = XK_Pause; break; + case GDK_Escape: k = XK_Escape; break; + case GDK_space: k = XK_space; break; + case GDK_Delete: k = XK_Delete; break; + case GDK_KP_0: k = XK_KP_0; break; + case GDK_KP_1: k = XK_KP_1; break; + case GDK_KP_2: k = XK_KP_2; break; + case GDK_KP_3: k = XK_KP_3; break; + case GDK_KP_4: k = XK_KP_4; break; + case GDK_KP_5: k = XK_KP_5; break; + case GDK_KP_6: k = XK_KP_6; break; + case GDK_KP_7: k = XK_KP_7; break; + case GDK_KP_8: k = XK_KP_8; break; + case GDK_KP_9: k = XK_KP_9; break; + case GDK_KP_Decimal: k = XK_KP_Decimal; break; + case GDK_KP_Divide: k = XK_KP_Divide; break; + case GDK_KP_Multiply: k = XK_KP_Multiply; break; + case GDK_KP_Subtract: k = XK_KP_Subtract; break; + case GDK_KP_Add: k = XK_KP_Add; break; + case GDK_KP_Enter: k = XK_KP_Enter; break; + case GDK_KP_Equal: k = XK_KP_Equal; break; + case GDK_Up: k = XK_Up; break; + case GDK_Down: k = XK_Down; break; + case GDK_Right: k = XK_Right; break; + case GDK_Left: k = XK_Left; break; + case GDK_Insert: k = XK_Insert; break; + case GDK_Home: k = XK_Home; break; + case GDK_End: k = XK_End; break; + case GDK_Page_Up: k = XK_Page_Up; break; + case GDK_Page_Down: k = XK_Page_Down; break; + case GDK_F1: k = XK_F1; break; + case GDK_F2: k = XK_F2; break; + case GDK_F3: k = XK_F3; break; + case GDK_F4: k = XK_F4; break; + case GDK_F5: k = XK_F5; break; + case GDK_F6: k = XK_F6; break; + case GDK_F7: k = XK_F7; break; + case GDK_F8: k = XK_F8; break; + case GDK_F9: k = XK_F9; break; + case GDK_F10: k = XK_F10; break; + case GDK_F11: k = XK_F11; break; + case GDK_F12: k = XK_F12; break; + case GDK_F13: k = XK_F13; break; + case GDK_F14: k = XK_F14; break; + case GDK_F15: k = XK_F15; break; + case GDK_Num_Lock: k = XK_Num_Lock; break; + case GDK_Caps_Lock: k = XK_Caps_Lock; break; + case GDK_Scroll_Lock: k = XK_Scroll_Lock; break; + case GDK_Shift_R: k = XK_Shift_R; break; + case GDK_Shift_L: k = XK_Shift_L; break; + case GDK_Control_R: k = XK_Control_R; break; + case GDK_Control_L: k = XK_Control_L; break; + case GDK_Alt_R: k = XK_Alt_R; break; + case GDK_Alt_L: k = XK_Alt_L; break; + case GDK_Meta_R: k = XK_Meta_R; break; + case GDK_Meta_L: k = XK_Meta_L; break; +#if 0 + /* TODO: find out keysyms */ + case GDK_Super_L: k = XK_LSuper; break; /* left "windows" key */ + case GDK_Super_R: k = XK_RSuper; break; /* right "windows" key */ + case GDK_Multi_key: k = XK_Compose; break; +#endif + case GDK_Mode_switch: k = XK_Mode_switch; break; + case GDK_Help: k = XK_Help; break; + case GDK_Print: k = XK_Print; break; + case GDK_Sys_Req: k = XK_Sys_Req; break; + case GDK_Break: k = XK_Break; break; + default: break; + } + if (k == 0) { + if (keyval < 0x100) + k = keyval; + else + rfbClientLog ("Unknown keysym: %d\n", keyval); + } + + return k; +} + +static gboolean key_event (GtkWidget *widget, GdkEventKey *event, + gpointer user_data) +{ + if ((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_F8)) + show_popup_menu(); + else + SendKeyEvent(cl, gdkKey2rfbKeySym (event->keyval), + (event->type == GDK_KEY_PRESS) ? TRUE : FALSE); + return FALSE; +} + +void quit () +{ + exit (0); +} + +static rfbBool resize (rfbClient *client) { + GtkWidget *window; + GtkWidget *scrolled_window; + GtkWidget *drawing_area=NULL; + static char first=TRUE; + int tmp_width, tmp_height; + + if (first) { + first=FALSE; + + /* Create the drawing area */ + + drawing_area = gtk_drawing_area_new (); + gtk_widget_set_size_request (GTK_WIDGET (drawing_area), + client->width, client->height); + + /* Signals used to handle backing pixmap */ + + g_signal_connect (G_OBJECT (drawing_area), "expose_event", + G_CALLBACK (expose_event), NULL); + + /* Event signals */ + + g_signal_connect (G_OBJECT (drawing_area), + "motion-notify-event", + G_CALLBACK (motion_notify_event), NULL); + g_signal_connect (G_OBJECT (drawing_area), + "button-press-event", + G_CALLBACK (button_event), NULL); + g_signal_connect (G_OBJECT (drawing_area), + "button-release-event", + G_CALLBACK (button_event), NULL); + + gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK + | GDK_LEAVE_NOTIFY_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK); + + gtk_widget_show (drawing_area); + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport ( + GTK_SCROLLED_WINDOW (scrolled_window), + drawing_area); + g_signal_connect (G_OBJECT (scrolled_window), + "key-press-event", G_CALLBACK (key_event), + NULL); + g_signal_connect (G_OBJECT (scrolled_window), + "key-release-event", G_CALLBACK (key_event), + NULL); + gtk_widget_show (scrolled_window); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), client->desktopName); + gtk_container_add (GTK_CONTAINER (window), scrolled_window); + tmp_width = (int) ( + gdk_screen_get_width (gdk_screen_get_default ()) + * 0.85); + if (client->width > tmp_width) { + tmp_height = (int) ( + gdk_screen_get_height ( + gdk_screen_get_default ()) + * 0.85); + gtk_widget_set_size_request (window, + tmp_width, tmp_height); + } else { + gtk_widget_set_size_request (window, + client->width + 2, + client->height + 2); + } + + g_signal_connect (G_OBJECT (window), "destroy", + G_CALLBACK (quit), NULL); + + gtk_widget_show (window); + } else { + gtk_widget_set_size_request (GTK_WIDGET (drawing_area), + client->width, client->height); + } + + return TRUE; +} + +static void update (rfbClient *cl, int x, int y, int w, int h) { + GtkWidget *drawing_area = rfbClientGetClientData (cl, gtk_init); + + if (drawing_area != NULL) + gtk_widget_queue_draw_area (drawing_area, x, y, w, h); +} + +static void kbd_leds (rfbClient *cl, int value, int pad) { + /* note: pad is for future expansion 0=unused */ + fprintf (stderr, "Led State= 0x%02X\n", value); + fflush (stderr); +} + +/* trivial support for textchat */ +static void text_chat (rfbClient *cl, int value, char *text) { + switch (value) { + case rfbTextChatOpen: + fprintf (stderr, "TextChat: We should open a textchat window!\n"); + TextChatOpen (cl); + break; + case rfbTextChatClose: + fprintf (stderr, "TextChat: We should close our window!\n"); + break; + case rfbTextChatFinished: + fprintf (stderr, "TextChat: We should close our window!\n"); + break; + default: + fprintf (stderr, "TextChat: Received \"%s\"\n", text); + break; + } + fflush (stderr); +} + +static gboolean on_entry_key_press_event (GtkWidget *widget, GdkEventKey *event, + gpointer user_data) +{ + if (event->keyval == GDK_Escape) + gtk_dialog_response (GTK_DIALOG(user_data), GTK_RESPONSE_REJECT); + else if (event->keyval == GDK_Return) + gtk_dialog_response (GTK_DIALOG(user_data), GTK_RESPONSE_ACCEPT); + + return FALSE; +} + +static void GtkErrorLog (const char *format, ...) +{ + GtkWidget *dialog, *label; + va_list args; + char buf[256]; + + if (dialog_connecting != NULL) { + gtk_widget_destroy (dialog_connecting); + dialog_connecting = NULL; + } + + va_start (args, format); + vsnprintf (buf, 255, format, args); + va_end (args); + + if (g_utf8_validate (buf, strlen (buf), NULL)) { + label = gtk_label_new (buf); + } else { + const gchar *charset; + gchar *utf8; + + (void) g_get_charset (&charset); + utf8 = g_convert_with_fallback (buf, strlen (buf), "UTF-8", + charset, NULL, NULL, NULL, NULL); + + if (utf8) { + label = gtk_label_new (utf8); + g_free (utf8); + } else { + label = gtk_label_new (buf); + g_warning ("Message Output is not in UTF-8" + "nor in locale charset.\n"); + } + } + + dialog = gtk_dialog_new_with_buttons ("Error", + NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + label = gtk_label_new (buf); + gtk_widget_show (label); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), + label); + gtk_widget_show (dialog); + + switch (gtk_dialog_run (GTK_DIALOG (dialog))) { + case GTK_RESPONSE_ACCEPT: + break; + default: + break; + } + gtk_widget_destroy (dialog); +} + +static void GtkDefaultLog (const char *format, ...) +{ + va_list args; + char buf[256]; + time_t log_clock; + + va_start (args, format); + + time (&log_clock); + strftime (buf, 255, "%d/%m/%Y %X ", localtime (&log_clock)); + fprintf (stdout, buf); + + vfprintf (stdout, format, args); + fflush (stdout); + + va_end (args); +} + +static char * get_password (rfbClient *client) +{ + GtkWidget *dialog, *entry; + char *password; + + gtk_widget_destroy (dialog_connecting); + dialog_connecting = NULL; + + dialog = gtk_dialog_new_with_buttons ("Password", + NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + entry = gtk_entry_new (); + gtk_entry_set_visibility (GTK_ENTRY (entry), + FALSE); + g_signal_connect (GTK_OBJECT(entry), "key-press-event", + G_CALLBACK(on_entry_key_press_event), + GTK_OBJECT (dialog)); + gtk_widget_show (entry); + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), + entry); + gtk_widget_show (dialog); + + switch (gtk_dialog_run (GTK_DIALOG (dialog))) { + case GTK_RESPONSE_ACCEPT: + password = strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + break; + default: + password = NULL; + break; + } + gtk_widget_destroy (dialog); + return password; +} + +int main (int argc, char *argv[]) +{ + int i; + GdkImage *image; + + rfbClientLog = GtkDefaultLog; + rfbClientErr = GtkErrorLog; + + gtk_init (&argc, &argv); + + /* create a dummy image just to make use of its properties */ + image = gdk_image_new (GDK_IMAGE_FASTEST, gdk_visual_get_system(), + 200, 100); + + cl = rfbGetClient (image->depth / 3, 3, image->bpp); + + cl->format.redShift = image->visual->red_shift; + cl->format.greenShift = image->visual->green_shift; + cl->format.blueShift = image->visual->blue_shift; + + cl->format.redMax = (1 << image->visual->red_prec) - 1; + cl->format.greenMax = (1 << image->visual->green_prec) - 1; + cl->format.blueMax = (1 << image->visual->blue_prec) - 1; + + g_object_unref (image); + + cl->MallocFrameBuffer = resize; + cl->canHandleNewFBSize = TRUE; + cl->GotFrameBufferUpdate = update; + cl->GotXCutText = got_cut_text; + cl->HandleKeyboardLedState = kbd_leds; + cl->HandleTextChat = text_chat; + cl->GetPassword = get_password; + + show_connect_window (argc, argv); + + if (!rfbInitClient (cl, &argc, argv)) + return 1; + + while (1) { + while (gtk_events_pending ()) + gtk_main_iteration (); + i = WaitForMessage (cl, 500); + if (i < 0) + return 0; + if (i && framebuffer_allocated == TRUE) + if (!HandleRFBServerMessage(cl)) + return 0; + } + + gtk_main (); + + return 0; +} + |