Hello community, here is the log from the commit of package headmore for openSUSE:Factory checked in at 2016-11-03 11:14:10 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/headmore (Old) and /work/SRC/openSUSE:Factory/.headmore.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "headmore" Changes: -------- --- /work/SRC/openSUSE:Factory/headmore/headmore.changes 2016-10-31 09:54:53.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.headmore.new/headmore.changes 2016-11-03 11:14:12.000000000 +0100 @@ -1,0 +2,11 @@ +Tue Nov 1 14:19:32 UTC 2016 - h...@suse.com + +- Upgrade to release 1.1.1 with accumulated feature enhancements: + * Do not use the log file (.headmore.log) under home directory + anymore. Connection log is now directly printed in terminal. + * Use F10 key instead of escape key to quit the viewer. + * Draw VNC mouse cursor locally when zoomed out far. + * Improve handling of rapid input of Alt key combinations. + * Improve handling of viewer controls at lower FPS. + +------------------------------------------------------------------- Old: ---- headmore-1.0.tar.gz New: ---- headmore-1.1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ headmore.spec ++++++ --- /var/tmp/diff_new_pack.gE8dyp/_old 2016-11-03 11:14:15.000000000 +0100 +++ /var/tmp/diff_new_pack.gE8dyp/_new 2016-11-03 11:14:15.000000000 +0100 @@ -1,6 +1,7 @@ # # spec file for package headmore # +# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2016 Howard Guo <guohou...@gmail.com> # # All modifications and additions to the file contributed by third parties @@ -15,17 +16,22 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # + Name: headmore -Version: 1.0 +Version: 1.1.1 Release: 0 -License: GPL-3.0 Summary: VNC client for character terminals -Url: https://github.com/HouzuoGuo/headmore +License: GPL-3.0 Group: Productivity/Networking/Other +Url: https://github.com/HouzuoGuo/headmore Source: %{name}-%{version}.tar.gz +BuildRequires: libgcrypt-devel +BuildRequires: libjpeg8-devel +BuildRequires: libopenssl-devel +BuildRequires: libpng16-compat-devel BuildRequires: pkg-config -BuildRequires: pkgconfig(caca) pkgconfig(libvncclient) -BuildRequires: libgcrypt-devel libjpeg8-devel libopenssl-devel libpng16-compat-devel +BuildRequires: pkgconfig(caca) +BuildRequires: pkgconfig(libvncclient) %description headmore is a client for Virtual Network Computing (VNC), @@ -38,14 +44,12 @@ %setup -q %build -gzip %{name}.1 +gzip headmore.1 make %install -mkdir -p %{buildroot}/%{_bindir}/ -install -m 0755 %{name} %{buildroot}/%{_bindir}/%{name} -mkdir -p %{buildroot}/%{_mandir}/man1/ -install -m 0644 %{name}.1.gz %{buildroot}/%{_mandir}/man1/%{name}.1.gz +install -D -m 0755 %{name} %{buildroot}/%{_bindir}/%{name} +install -D -m 0644 %{name}.1.gz %{buildroot}/%{_mandir}/man1/%{name}.1.gz %files %defattr(-,root,root) ++++++ headmore-1.0.tar.gz -> headmore-1.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/headmore-1.0/geo.c new/headmore-1.1.1/geo.c --- old/headmore-1.0/geo.c 1970-01-01 01:00:00.000000000 +0100 +++ new/headmore-1.1.1/geo.c 2016-11-02 09:26:31.000000000 +0100 @@ -0,0 +1,150 @@ +#include <string.h> +#include "geo.h" + +struct geo_facts geo_facts_of(struct vnc *vnc, caca_display_t * disp, + caca_canvas_t * view) +{ + struct geo_facts ret; + ret.px_width = caca_get_display_width(disp); + ret.px_height = caca_get_display_height(disp); + ret.ch_width = caca_get_canvas_width(view); + ret.ch_height = caca_get_canvas_height(view); + ret.vnc_width = vnc->conn->width; + ret.vnc_height = vnc->conn->height; + return ret; +} + +void geo_init(struct geo *g, struct geo_facts facts) +{ + memset(g, 0, sizeof(struct geo)); + /* Fill the zoom table */ + int i; + g->zoom_lvls[0] = 1.0; + for (i = 0; i < GEO_ZOOM_MAX_LVL; i++) { + g->zoom_lvls[i + 1] = g->zoom_lvls[i] * GEO_ZOOM_STEP; + } + /* + * Mouse speed is the inverse of zoom. + * At maximum zoom, mouse moves slowest at speed of 1px/move. + * Each zoom step lower speeds up mouse movement by 2px/move. + */ + for (i = 0; i < GEO_ZOOM_MAX_LVL + 1; i++) { + g->mouse_speed[i] = 1 + (GEO_ZOOM_MAX_LVL - i) * 2; + } + + /* Begin by placing image right at the centre */ + g->view_x = 0.5; + g->view_y = 0.5; + geo_zoom(g, facts, 0); + + /* Place mouse pointer at centre of screen and depress all buttons */ + g->mouse_x = facts.vnc_width / 2; + g->mouse_y = facts.vnc_height / 2; +} + +void geo_zoom(struct geo *g, struct geo_facts facts, int offset) +{ + g->zoom += offset; + if (g->zoom < 0) { + g->zoom = 0; + } else if (g->zoom > GEO_ZOOM_MAX_LVL) { + g->zoom = GEO_ZOOM_MAX_LVL; + } + + g->zoom_x = + (g->zoom < + 0) ? 1.0 / g->zoom_lvls[-g->zoom] : g->zoom_lvls[g->zoom]; + g->zoom_y = + g->zoom_x * facts.ch_width / facts.ch_height * + facts.vnc_height / facts.vnc_width * facts.ch_height / + facts.ch_width * facts.px_width / facts.px_height; + + if (g->zoom_y > g->zoom_x) { + float tmp = g->zoom_x; + g->zoom_x = tmp * tmp / g->zoom_y; + g->zoom_y = tmp; + } +} + +void geo_pan(struct geo *g, int pan_x, int pan_y) +{ + if (pan_x != 0) { + if (g->zoom_x > 1.0) { + g->view_x += pan_x * (GEO_PAN_STEP / g->zoom_x); + } + if (g->view_x < 0.0) { + g->view_x = 0.0; + } else if (g->view_x > 1.0) { + g->view_x = 1.0; + } + } + if (pan_y != 0) { + if (g->zoom_y > 1.0) { + g->view_y += pan_y * (GEO_PAN_STEP / g->zoom_y); + } + if (g->view_y < 0.0) { + g->view_y = 0.0; + } else if (g->view_y > 1.0) { + g->view_y = 1.0; + } + } +} + +void geo_move_mouse(struct geo *g, struct geo_facts facts, int step_x, + int step_y) +{ + int speed = g->mouse_speed[g->zoom]; + g->mouse_x += step_x * speed; + if (g->mouse_x < 0) { + g->mouse_x = 0; + } else if (g->mouse_x >= facts.vnc_width) { + g->mouse_x = facts.vnc_width - 1; + } + g->mouse_y += step_y * speed; + if (g->mouse_y < 0) { + g->mouse_y = 0; + } else if (g->mouse_y >= facts.vnc_height) { + g->mouse_y = facts.vnc_height - 1; + } +} + +void geo_zoom_to_cursor(struct geo *g, struct geo_facts facts) +{ + /* Zoom in to approximately 67% of maximum zoom level */ + geo_zoom(g, facts, GEO_ZOOM_CURSOR_LVL - g->zoom); + /* Calculate position of mouse cursor relative to the entire canvas */ + g->view_x = (float)g->mouse_x / (float)facts.vnc_width; + g->view_y = (float)g->mouse_y / (float)facts.vnc_height; +} + +struct geo_dither_params geo_get_dither_params(struct geo *g, + struct geo_facts facts) +{ + struct geo_dither_params ret; + ret.facts = facts; + float delta_x = (g->zoom_x > 1.0) ? g->view_x : 0.5; + float delta_y = (g->zoom_y > 1.0) ? g->view_y : 0.5; + ret.x = facts.ch_width * (1.0 - g->zoom_x) * delta_x; + ret.y = facts.ch_height * (1.0 - g->zoom_y) * delta_y; + ret.width = facts.ch_width * g->zoom_x + 1; + ret.height = facts.ch_height * g->zoom_y + 1; + return ret; +} + +int geo_dither_ch_px_x(struct geo_dither_params *params, int px_x) +{ + float dx = (float)px_x / params->facts.vnc_width; + return dx * params->width + params->x; +} + +int geo_dither_ch_px_y(struct geo_dither_params *params, int px_y) +{ + float dy = (float)px_y / params->facts.vnc_height; + return dy * params->height + params->y; +} + +int geo_dither_numch_x(struct geo_dither_params *params, int num_pixels_x) +{ + return geo_dither_ch_px_x(params, + num_pixels_x) - geo_dither_ch_px_x(params, 0); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/headmore-1.0/geo.h new/headmore-1.1.1/geo.h --- old/headmore-1.0/geo.h 1970-01-01 01:00:00.000000000 +0100 +++ new/headmore-1.1.1/geo.h 2016-11-02 09:26:31.000000000 +0100 @@ -0,0 +1,60 @@ +#ifndef GEO_H +#define GEO_H + +#include <caca.h> +#include "vnc.h" + +#define GEO_PAN_STEP 0.20 +#define GEO_ZOOM_STEP 1.20f +#define GEO_ZOOM_MAX_LVL 15 +#define GEO_ZOOM_CURSOR_LVL 11 /* zoom level for zooming into mouse cursor */ + +/* Geometry facts of terminal emulator and VNC connection. */ +struct geo_facts { + int px_width, px_height; + int ch_width, ch_height; + int vnc_width, vnc_height; +}; + +/* Return geometry facts of the VNC connection and caca terminal. */ +struct geo_facts geo_facts_of(struct vnc *vnc, caca_display_t * disp, + caca_canvas_t * canvas); + +/* Keep track of geometry of remote frame-buffer and canvas. */ +struct geo { + float view_x, zoom_x, view_y, zoom_y; + int zoom; + float zoom_lvls[GEO_ZOOM_MAX_LVL + 1]; + + int mouse_speed[GEO_ZOOM_MAX_LVL + 1]; + int mouse_x, mouse_y; +}; + +/* Initialise geometry structure. */ +void geo_init(struct geo *g, struct geo_facts facts); +/* Calculate geometry after zooming in (+) or out (-). */ +void geo_zoom(struct geo *g, struct geo_facts facts, int offset); +/* Pan the canvas several steps up (-y), down (+y), left (-x), or right (+x). */ +void geo_pan(struct geo *g, int pan_x, int pan_y); +/* Move mouse pointer several steps up (-y) down (+y), left (-x) or right (+x).*/ +void geo_move_mouse(struct geo *g, struct geo_facts facts, int step_x, + int step_y); +/* Move view to the location of mouse cursor and zoom in there. */ +void geo_zoom_to_cursor(struct geo *g, struct geo_facts facts); + +/* Calculated parameters for dithering algorithm. */ +struct geo_dither_params { + struct geo_facts facts; + int x, y, width, height; +}; +/* Calculate and return input parameters for dithering algorithm. */ +struct geo_dither_params geo_get_dither_params(struct geo *g, + struct geo_facts facts); +/* Return the the character location of the VNC pixel on X axis. */ +int geo_dither_ch_px_x(struct geo_dither_params *params, int px_x); +/* Return the the character location of the VNC pixel on Y axis. */ +int geo_dither_ch_px_y(struct geo_dither_params *params, int px_y); +/* Return (approx.) number of characters it would take to draw those pixels on X axis. */ +int geo_dither_numch_x(struct geo_dither_params *params, int num_pixels_x); + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/headmore-1.0/headmore.1 new/headmore-1.1.1/headmore.1 --- old/headmore-1.0/headmore.1 2016-10-18 09:22:01.000000000 +0200 +++ new/headmore-1.1.1/headmore.1 2016-11-02 09:26:31.000000000 +0100 @@ -19,10 +19,6 @@ For QWERTY keyboard users, the left hand side controls are: .TP -.B Escape -Disconnect from VNC server and leave the viewer immediately. -. -.TP .B Back-tick (`) Switch keyboard input between viewer/mouse control and VNC desktop. When you have just connected, your keyboard input is directed at viewer/mouse control. . @@ -43,6 +39,10 @@ And the right hand side controls are: .TP +.B F10 +Disconnect from VNC server and leave the viewer immediately. +. +.TP .B I, J, K, L Move mouse cursor up, left, down, or right. Cursor speed is determined by current zoom, it move slower when zoomed in. . @@ -85,14 +85,20 @@ .SH INPUT QUIRKS -While nearly all keyboard input will be successfully sent to VNC desktop, there are however several quirks to bear in mind: +While nearly all keyboard input will be successfully sent to VNC desktop, bear in mind several quirks caused by limitations of character terminal, universal to all terminal emulators: .IP \[bu] -If you have a computer mouse and your terminal emulator somehow forwards mouse clicks to headmore, headmore may do random things or quit unexpectedly. +If you have a computer mouse and your terminal forwards mouse input headmore, headmore may react with random key input or quit unexpectedly. +.IP \[bu] +Meta key combinations such as Meta+D cannot be directly typed into VNC, you have to toggle hold Meta key and then type the modified key. +.IP \[bu] +Other modifier keys (Control and Alt) only work with lower case letters a-z. For other combinations such as Alt+F4 or Ctrl+Shift+A, toggle hold the modifier key and then press the modified key. +.IP \[bu] +Rapid input of Alt key combinations may occasionally result in the modified being typed without Alt modifier. Type Alt key combinations slowly to avoid encountering this issue. .IP \[bu] -All modifier keys only work with lower case letters a-z. For other combinations such as Alt+F4, toggle hold Alt key and then type F4 in VNC. +Typing escape key and another key in short succession may result in an Alt combination being typed unexpectedly. Wait a short while after the escape key to avoid encountering this issue. .IP \[bu] -While most control characters such as Ctrl+C and Ctrl+D work reliably in VNC input, a few of them (such as Ctrl+Z) do not work in certain terminal emulators, you have to toggle hold control key and then press the other character individually. +While most control characters such as Ctrl+C and Ctrl+D work reliably in VNC input, a few of them (such as Ctrl+Z) do not work in certain terminal emulators, in such cases you have to toggle hold Ctrl and then press the letter key. .SH DESIGN RATIONALE .B headmore diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/headmore-1.0/viewer.c new/headmore-1.1.1/viewer.c --- old/headmore-1.0/viewer.c 2016-10-18 09:22:01.000000000 +0200 +++ new/headmore-1.1.1/viewer.c 2016-11-02 09:26:31.000000000 +0100 @@ -18,12 +18,12 @@ /* Messages to display in a static help menu. */ static char const *viewer_help[] = { "============ LEFT HAND ============", - "Esc Disconnect and quit ", "` Toggle input to viewer/VNC ", "~ Click back-tick in VNC ", "wasd Pan viewer ", "q/e Zoom out/in ", "============ RIGHT HAND ===========", + "F10 Quit ", "ijkl Move mouse cursor ", "u/o Click L/R mouse button ", "789 Toggle hold L/M/R mouse button", @@ -38,104 +38,91 @@ }; /* Return the RFB client the viewer is connected to. */ -static struct _rfbClient *rfb(struct viewer *viewer) +static struct _rfbClient *rfb(struct viewer *v) { - return viewer->vnc->conn; + return v->vnc->conn; } -bool viewer_init(struct viewer * viewer, struct vnc * vnc) +bool viewer_init(struct viewer * v, struct vnc * vnc) { - memset(viewer, 0, sizeof(struct viewer)); - /* Fill the zoom table */ - int i; - viewer->zoom_lvls[0] = 1.0; - for (i = 0; i < VIEWER_ZOOM_MAX_LVL; i++) { - viewer->zoom_lvls[i + 1] = - viewer->zoom_lvls[i] * VIEWER_ZOOM_STEP; - } - /* - * Mouse speed is the inverse of zoom. - * At maximum zoom, mouse moves slowest at speed of 1px/move. - * Each zoom step lower speeds up mouse movement by 2px/move. - */ - for (i = 0; i < VIEWER_ZOOM_MAX_LVL + 1; i++) { - viewer->mouse_speed[i] = 1 + (VIEWER_ZOOM_MAX_LVL - i) * 2; - } - viewer->view = caca_create_canvas(0, 0); - if (!viewer->view) { + memset(v, 0, sizeof(struct viewer)); + /* Initialise visuals */ + v->view = caca_create_canvas(0, 0); + if (!v->view) { fprintf(stderr, "Failed to create caca canvas\n"); return false; } - viewer->disp = caca_create_display_with_driver(viewer->view, "ncurses"); - if (!viewer->disp) { + v->disp = caca_create_display_with_driver(v->view, "ncurses"); + if (!v->disp) { fprintf(stderr, "Failed to create caca display\n"); return false; } - viewer->vnc = vnc; - caca_set_display_title(viewer->disp, rfb(viewer)->desktopName); + v->vnc = vnc; + caca_set_display_title(v->disp, rfb(v)->desktopName); + + /* Initialise parameters for geometry calculation */ + geo_init(&v->geo, viewer_geo(v)); - /* Begin by placing image right at the centre */ - viewer->view_x = 0.5; - viewer->view_y = 0.5; - viewer_zoom(viewer, 0); - - /* Place mouse pointer at centre of screen and depress all buttons */ - viewer->mouse_x = rfb(viewer)->width / 2; - viewer->mouse_y = rfb(viewer)->height / 2; - viewer_vnc_send_pointer(viewer); + /* Tell VNC to place mouse pointer to default position */ + viewer_vnc_send_pointer(v); return true; } -void viewer_disp_status(struct viewer *viewer) +struct geo_facts viewer_geo(struct viewer *v) { - caca_set_color_ansi(viewer->view, CACA_WHITE, CACA_BLUE); + return geo_facts_of(v->vnc, v->disp, v->view); +} + +void viewer_disp_status(struct viewer *v) +{ + caca_set_color_ansi(v->view, CACA_WHITE, CACA_BLUE); char *conn_remark = ""; - if (!viewer->vnc->connected) { + if (!v->vnc->connected) { conn_remark = "(Disconnected)"; } char *who_has_input = "Input to viewer"; - if (viewer->input2vnc) { + if (v->input2vnc) { who_has_input = "Input to VNC (~ to disengage)"; } char held_controls[80] = { 0 }; bool disp_held_controls = false; - if (viewer->mouse_left) { + if (v->mouse_left) { disp_held_controls = true; strcat(held_controls, " LMouse"); } - if (viewer->mouse_middle) { + if (v->mouse_middle) { disp_held_controls = true; strcat(held_controls, " MMouse"); } - if (viewer->mouse_right) { + if (v->mouse_right) { disp_held_controls = true; strcat(held_controls, " RMouse"); } - if (viewer->hold_lctrl) { + if (v->hold_lctrl) { disp_held_controls = true; strcat(held_controls, " LCtrl"); } - if (viewer->hold_lshift) { + if (v->hold_lshift) { disp_held_controls = true; strcat(held_controls, " LShift"); } - if (viewer->hold_lalt) { + if (v->hold_lalt) { disp_held_controls = true; strcat(held_controls, " LAlt"); } - if (viewer->hold_lsuper) { + if (v->hold_lsuper) { disp_held_controls = true; strcat(held_controls, " LSuper"); } - if (viewer->hold_ralt) { + if (v->hold_ralt) { disp_held_controls = true; strcat(held_controls, " RAlt"); } - if (viewer->hold_rshift) { + if (v->hold_rshift) { disp_held_controls = true; strcat(held_controls, " RShift"); } - if (viewer->hold_rctrl) { + if (v->hold_rctrl) { disp_held_controls = true; strcat(held_controls, " RCtrl"); } @@ -144,225 +131,136 @@ strcat(held_controls_msg, "| Holding down:"); strcat(held_controls_msg, held_controls); } - caca_printf(viewer->view, 0, 0, "h:Help | %s:%d%s | %s %s", - rfb(viewer)->serverHost, rfb(viewer)->serverPort, - conn_remark, who_has_input, held_controls_msg); + caca_printf(v->view, 0, 0, "h:Help | %s:%d%s | %s %s", + rfb(v)->serverHost, rfb(v)->serverPort, conn_remark, + who_has_input, held_controls_msg); } -void viewer_disp_help(struct viewer *viewer) +void viewer_disp_help(struct viewer *v) { - caca_set_color_ansi(viewer->view, CACA_WHITE, CACA_BLUE); + caca_set_color_ansi(v->view, CACA_WHITE, CACA_BLUE); int i; for (i = 0; viewer_help[i] != NULL; i++) { - caca_put_str(viewer->view, 0, 1 + i, viewer_help[i]); + caca_put_str(v->view, 0, 1 + i, viewer_help[i]); } } -void viewer_zoom(struct viewer *viewer, int offset) +void viewer_redraw(struct viewer *v) { - viewer->zoom_lvl += offset; - if (viewer->zoom_lvl < 0) { - viewer->zoom_lvl = 0; - } else if (viewer->zoom_lvl > VIEWER_ZOOM_MAX_LVL) { - viewer->zoom_lvl = VIEWER_ZOOM_MAX_LVL; - } - - int viewer_width = caca_get_canvas_width(viewer->view); - int viewer_height = caca_get_canvas_height(viewer->view); - viewer->zoom_x = - (viewer->zoom_lvl < - 0) ? 1.0 / - viewer->zoom_lvls[-viewer->zoom_lvl] : viewer-> - zoom_lvls[viewer->zoom_lvl]; - viewer->zoom_y = - viewer->zoom_x * viewer_width / viewer_height * - rfb(viewer)->height / rfb(viewer)->width * viewer_height / - viewer_width * caca_get_display_width(viewer->disp) / - caca_get_display_height(viewer->disp); - - if (viewer->zoom_y > viewer->zoom_x) { - float tmp = viewer->zoom_x; - viewer->zoom_x = tmp * tmp / viewer->zoom_y; - viewer->zoom_y = tmp; - } -} - -void viewer_pan(struct viewer *viewer, int pan_x, int pan_y) -{ - if (pan_x != 0) { - if (viewer->zoom_x > 1.0) { - viewer->view_x += - pan_x * (VIEWER_PAN_STEP / viewer->zoom_x); - } - if (viewer->view_x < 0.0) { - viewer->view_x = 0.0; - } else if (viewer->view_x > 1.0) { - viewer->view_x = 1.0; - } - } - if (pan_y != 0) { - if (viewer->zoom_y > 1.0) { - viewer->view_y += - pan_y * (VIEWER_PAN_STEP / viewer->zoom_y); - } - if (viewer->view_y < 0.0) { - viewer->view_y = 0.0; - } else if (viewer->view_y > 1.0) { - viewer->view_y = 1.0; - } - } -} - -void viewer_redraw(struct viewer *viewer) -{ - caca_set_color_ansi(viewer->view, CACA_WHITE, CACA_BLACK); - caca_clear_canvas(viewer->view); + caca_clear_canvas(v->view); /* * Run the latest frame-buffer content through Floyd–Steinberg algorithm - * it seems to offer higher quality over other algorithm choices. * The dither parameters are tailored for a connection on 32-bit RGB colours. */ - if (viewer->fb_dither != NULL) { - caca_free_dither(viewer->fb_dither); + if (v->fb_dither != NULL) { + caca_free_dither(v->fb_dither); + } + struct geo_facts facts = viewer_geo(v); + v->fb_dither = + caca_create_dither(32, facts.vnc_width, facts.vnc_height, + facts.vnc_width * 4, 0x000000ff, 0x0000ff00, + 0x00ff0000, 0); + caca_set_dither_algorithm(v->fb_dither, "fstein"); + caca_set_dither_gamma(v->fb_dither, 1.0); + struct geo_dither_params params = + geo_get_dither_params(&v->geo, viewer_geo(v)); + caca_dither_bitmap(v->view, params.x, params.y, params.width, + params.height, v->fb_dither, rfb(v)->frameBuffer); + /* + * Mouse cursors are usually wider than 14 pixels. If it will not take + * more than 5 characters to draw the cusor, then consider it very + * difficult to spot on the VNC canvas, and draw an easy to spot block + * right there. + */ + if (geo_dither_numch_x(¶ms, 12) < 5) { + caca_set_color_ansi(v->view, CACA_WHITE, CACA_RED); + int ch_x = geo_dither_ch_px_x(¶ms, v->geo.mouse_x); + int ch_y = geo_dither_ch_px_y(¶ms, v->geo.mouse_y); + caca_fill_box(v->view, ch_x - 1, ch_y - 1, 3, 3, '*'); } - viewer->fb_dither = - caca_create_dither(32, rfb(viewer)->width, - rfb(viewer)->height, - rfb(viewer)->width * 4, 0x000000ff, - 0x0000ff00, 0x00ff0000, 0); - caca_set_dither_algorithm(viewer->fb_dither, "fstein"); - caca_set_dither_gamma(viewer->fb_dither, 1.0); - int viewer_width = caca_get_canvas_width(viewer->view); - int viewer_height = caca_get_canvas_height(viewer->view); - float delta_x = (viewer->zoom_x > 1.0) ? viewer->view_x : 0.5; - float delta_y = (viewer->zoom_y > 1.0) ? viewer->view_y : 0.5; - - /* Draw frame-buffer and other things on the canvas */ - caca_dither_bitmap(viewer->view, - viewer_width * (1.0 - - viewer->zoom_x) * delta_x, - viewer_height * (1.0 - - viewer->zoom_y) * delta_y, - viewer_width * viewer->zoom_x + 1, - viewer_height * viewer->zoom_y + 1, - viewer->fb_dither, rfb(viewer)->frameBuffer); - viewer_disp_status(viewer); - if (viewer->disp_help) { - viewer_disp_help(viewer); + viewer_disp_status(v); + if (v->disp_help) { + viewer_disp_help(v); } - caca_refresh_display(viewer->disp); + caca_refresh_display(v->disp); } -void viewer_ev_loop(struct viewer *viewer) +void viewer_ev_loop(struct viewer *v) { + int ev_accept = + CACA_EVENT_KEY_PRESS | CACA_EVENT_RESIZE | CACA_EVENT_QUIT; while (true) { /* Listen to the latest event */ caca_event_t ev; - caca_get_event(viewer->disp, - CACA_EVENT_KEY_PRESS | CACA_EVENT_RESIZE | - CACA_EVENT_QUIT, &ev, 1000000 / VIEWER_FPS); + caca_get_event(v->disp, ev_accept, &ev, + VIEWER_FRAME_INTVL_USEC); /* Certain types of events are caca calling quit */ enum caca_event_type ev_type = caca_get_event_type(&ev); if (ev_type & CACA_EVENT_QUIT || ev_type & CACA_EVENT_NONE) { return; } /* Handle previously banked escape key (VNC input), send it to VNC. */ - if (viewer->last_vnc_esc != 0 - && get_time_usec() - viewer->last_vnc_esc >= - 1000000 / VIEWER_FPS) { - viewer->last_vnc_esc = 0; - viewer_vnc_click_key(viewer, - cacakey2vnc(CACA_KEY_ESCAPE)); - } - /* Handle previously banked escape key (viewer control), stop the viewer. */ - if (viewer->last_viewer_esc != 0 - && get_time_usec() - viewer->last_viewer_esc >= - 1000000 / VIEWER_FPS) { - return; + if (v->last_vnc_esc != 0 + && get_time_usec() - v->last_vnc_esc >= + VIEWER_FRAME_INTVL_USEC) { + v->last_vnc_esc = 0; + viewer_vnc_click_key(v, cacakey2vnc(CACA_KEY_ESCAPE)); } /* Redraw at a constant frame rate when there is no key input */ if (!(ev_type & CACA_EVENT_KEY_PRESS)) { - viewer_redraw(viewer); + viewer_redraw(v); continue; } int ev_char = caca_get_event_key_ch(&ev); /* Input never gets directed at VNC if it is disconnected */ - if (!viewer->vnc->connected) { - viewer->input2vnc = false; + if (!v->vnc->connected) { + v->input2vnc = false; } /* A key input is directed at either VNC or viewer controls */ - if (viewer->input2vnc && ev_char != '`') { - viewer_input_to_vnc(viewer, ev_char); - } else { - viewer_handle_control(viewer, ev_char); + if (v->input2vnc && ev_char != '`') { + viewer_input_to_vnc(v, ev_char); + } else if (!viewer_handle_control(v, ev_char)) { + return; } } } -void viewer_vnc_click_key(struct viewer *viewer, int vnc_key) +void viewer_vnc_click_key(struct viewer *v, int vnc_key) { - SendKeyEvent(rfb(viewer), vnc_key, TRUE); - SendKeyEvent(rfb(viewer), vnc_key, FALSE); + SendKeyEvent(rfb(v), vnc_key, TRUE); + SendKeyEvent(rfb(v), vnc_key, FALSE); } -void viewer_vnc_click_ctrl_key_combo(struct viewer *viewer, int vnc_key) +void viewer_vnc_click_ctrl_key_combo(struct viewer *v, int vnc_key) { - SendKeyEvent(rfb(viewer), XK_Control_L, TRUE); - SendKeyEvent(rfb(viewer), vnc_key, TRUE); - SendKeyEvent(rfb(viewer), vnc_key, FALSE); - SendKeyEvent(rfb(viewer), XK_Control_L, FALSE); + SendKeyEvent(rfb(v), XK_Control_L, TRUE); + SendKeyEvent(rfb(v), vnc_key, TRUE); + SendKeyEvent(rfb(v), vnc_key, FALSE); + SendKeyEvent(rfb(v), XK_Control_L, FALSE); } -void viewer_vnc_toggle_key(struct viewer *viewer, int vnc_key, bool key_down) +void viewer_vnc_toggle_key(struct viewer *v, int vnc_key, bool key_down) { - SendKeyEvent(rfb(viewer), vnc_key, key_down ? TRUE : FALSE); + SendKeyEvent(rfb(v), vnc_key, key_down ? TRUE : FALSE); } -void viewer_vnc_send_pointer(struct viewer *viewer) +void viewer_vnc_send_pointer(struct viewer *v) { int mask = 0; - if (viewer->mouse_left) { + if (v->mouse_left) { mask |= rfbButton1Mask; } - if (viewer->mouse_middle) { + if (v->mouse_middle) { mask |= rfbButton2Mask; } - if (viewer->mouse_right) { + if (v->mouse_right) { mask |= rfbButton3Mask; } - SendPointerEvent(rfb(viewer), viewer->mouse_x, viewer->mouse_y, mask); -} - -void viewer_move_mouse(struct viewer *viewer, int step_x, int step_y) -{ - int speed = viewer->mouse_speed[viewer->zoom_lvl]; - viewer->mouse_x += step_x * speed; - viewer->mouse_y += step_y * speed; - if (viewer->mouse_x < 0) { - viewer->mouse_x = 0; - } else if (viewer->mouse_x > rfb(viewer)->width - 1) { - viewer->mouse_x = rfb(viewer)->width - 1; - } - if (viewer->mouse_y < 0) { - viewer->mouse_y = 0; - } else if (viewer->mouse_y > rfb(viewer)->height - 1) { - viewer->mouse_y = rfb(viewer)->height - 1; - } - viewer_vnc_send_pointer(viewer); -} - -void viewer_zoom_to_cursor(struct viewer *viewer) -{ - /* Zoom in to approximately 67% of maximum zoom level */ - viewer_zoom(viewer, VIEWER_ZOOM_MAX_LVL * 2 / 3 - viewer->zoom_lvl); - /* Calculate position of mouse cursor relative to the entire canvas */ - viewer->view_x = (float)viewer->mouse_x / (float)rfb(viewer)->width; - viewer->view_y = (float)viewer->mouse_y / (float)rfb(viewer)->height; - /* Pan several steps to place the mouse cursor closer to view's centre */ - viewer_redraw(viewer); + SendPointerEvent(rfb(v), v->geo.mouse_x, v->geo.mouse_y, mask); } -void viewer_input_to_vnc(struct viewer *viewer, int caca_key) +void viewer_input_to_vnc(struct viewer *v, int caca_key) { /* * Escape key could mean two things, either it is a key press by itself, or it @@ -372,7 +270,7 @@ * dealt with later. */ if (caca_key == CACA_KEY_ESCAPE) { - viewer->last_vnc_esc = get_time_usec(); + v->last_vnc_esc = get_time_usec(); return; } /* @@ -404,24 +302,23 @@ case 24: case 25: case 26: /* (missing S) Ctrl+TUVWXYZ */ - viewer_vnc_click_ctrl_key_combo(viewer, - caca_key - 1 + 'a'); + viewer_vnc_click_ctrl_key_combo(v, caca_key - 1 + 'a'); return; case 8: /* H and an extra backspace */ - viewer->void_backsp = true; - viewer_vnc_click_ctrl_key_combo(viewer, 'h'); + v->void_backsp = true; + viewer_vnc_click_ctrl_key_combo(v, 'h'); return; case 9: /* I and an extra tab */ - viewer->void_tab = true; - viewer_vnc_click_ctrl_key_combo(viewer, 'i'); + v->void_tab = true; + viewer_vnc_click_ctrl_key_combo(v, 'i'); return; case 13: /* M and an extra enter */ - viewer->void_ret = true; - viewer_vnc_click_ctrl_key_combo(viewer, 'm'); + v->void_ret = true; + viewer_vnc_click_ctrl_key_combo(v, 'm'); return; case 19: /* S and an extra pause */ - viewer->void_pause = true; - viewer_vnc_click_ctrl_key_combo(viewer, 's'); + v->void_pause = true; + viewer_vnc_click_ctrl_key_combo(v, 's'); return; } } @@ -431,25 +328,25 @@ */ switch (caca_key) { case CACA_KEY_BACKSPACE: - if (viewer->void_backsp) { - viewer->void_backsp = false; + if (v->void_backsp) { + v->void_backsp = false; return; } break; case CACA_KEY_TAB: - if (viewer->void_tab) { - viewer->void_tab = false; + if (v->void_tab) { + v->void_tab = false; return; } break; case CACA_KEY_RETURN: - if (viewer->void_ret) { - viewer->void_ret = false; + if (v->void_ret) { + v->void_ret = false; return; } case CACA_KEY_PAUSE: - if (viewer->void_pause) { - viewer->void_pause = false; + if (v->void_pause) { + v->void_pause = false; return; } } @@ -460,175 +357,196 @@ return; } /* - * In case there was a banked escape key, the Alt combination key shall arrive - * in an instant within 2000 microseconds. + * In case there was a banked escape key, the Alt combination key shall + * arrive pretty soon, and most definitely before the next frame refresh. + * Occasionally it takes even longer to arrive but there is no way to + * work around it. */ - if (get_time_usec() - viewer->last_vnc_esc < 2000) { - viewer_vnc_toggle_key(viewer, XK_Alt_L, true); - viewer_vnc_click_key(viewer, translated_ch); - viewer_vnc_toggle_key(viewer, XK_Alt_L, false); - viewer->last_vnc_esc = 0; + suseconds_t elapsed = get_time_usec() - v->last_vnc_esc; + if (elapsed < VIEWER_FRAME_INTVL_USEC + && elapsed < VIEWER_MAX_INPUT_INTVL_USEC) { + viewer_vnc_toggle_key(v, XK_Alt_L, true); + viewer_vnc_click_key(v, translated_ch); + viewer_vnc_toggle_key(v, XK_Alt_L, false); + v->last_vnc_esc = 0; return; } /* Finally the key code can be sent to VNC! */ - viewer_vnc_click_key(viewer, translated_ch); + viewer_vnc_click_key(v, translated_ch); } -void viewer_handle_control(struct viewer *viewer, int caca_key) +bool viewer_handle_control(struct viewer * v, int caca_key) { /* - * Similar to the case with viewer_input_to_vnc, if user accidentally types an - * Alt key combination in the viewer, the first key event - an escape key will - * mislead viewer into quitting. - * Instead of quitting immediately, an escape key will be banked and the viewer - * awaits one more key event to arrive. If it indeed arrives and within 2000 - * microseconds, then the user must have mistakenly typed Alt key combination, - * in which case the viewer shall not quit. - * If the latter key event does not arrive, the escape key must have been an - * intention to quit the viewer and event loop will do that job. + * When the canvas is too busy panning/zooming, causing very low FPS, + * the terminal occasionally generates repeatitive key input seemingly + * out of nowhere, in very short successions. To work around it, this + * condition caps number of viewe controls to approximately 10 per second. */ - if (get_time_usec() - viewer->last_viewer_esc < 2000) { - viewer->last_viewer_esc = 0; + suseconds_t elapsed = get_time_usec() - v->last_viewer_control; + if (v->last_viewer_control != 0 + && elapsed < VIEWER_MAX_INPUT_INTVL_USEC) { + return true; } + /* + * In order to avoid redrawing too rapidly, only viewer zoom/pan + * actions redraw immediately. + * VNC input has to wait for main loop to redraw at an interval. + */ switch (caca_key) { case 'h': case 'H': - viewer->disp_help = !viewer->disp_help; - break; - case CACA_KEY_ESCAPE: - /* Escape key is banked, its meaning will be determined soon later, */ - viewer->last_viewer_esc = get_time_usec(); + v->disp_help = !v->disp_help; break; + case CACA_KEY_F10: + return false; /* Left hand */ case 'w': case 'W': - viewer_pan(viewer, 0, -1); + geo_pan(&v->geo, 0, -1); + viewer_redraw(v); break; case 'a': case 'A': - viewer_pan(viewer, -1, 0); + geo_pan(&v->geo, -1, 0); + viewer_redraw(v); break; case 's': case 'S': - viewer_pan(viewer, 0, 1); + geo_pan(&v->geo, 0, 1); + viewer_redraw(v); break; case 'd': case 'D': - viewer_pan(viewer, 1, 0); + geo_pan(&v->geo, 1, 0); + viewer_redraw(v); break; case 'q': case 'Q': - viewer_zoom(viewer, -1); + geo_zoom(&v->geo, viewer_geo(v), -1); + viewer_redraw(v); break; case 'e': case 'E': - viewer_zoom(viewer, 1); + geo_zoom(&v->geo, viewer_geo(v), 1); + viewer_redraw(v); break; case '`': - if (viewer->vnc->connected) { - viewer->input2vnc = !viewer->input2vnc; + if (v->vnc->connected) { + v->input2vnc = !v->input2vnc; } break; case '~': - viewer_vnc_click_key(viewer, 96); /* click back-tick (96 in ASCII) in VNC */ + /* Click back-tick (96 in ASCII) in VNC */ + viewer_vnc_click_key(v, 96); break; /* Right hand */ case 'i': case 'I': - viewer_move_mouse(viewer, 0, -1); + geo_move_mouse(&v->geo, viewer_geo(v), 0, -1); + viewer_vnc_send_pointer(v); break; case 'j': case 'J': - viewer_move_mouse(viewer, -1, 0); + geo_move_mouse(&v->geo, viewer_geo(v), -1, 0); + viewer_vnc_send_pointer(v); break; case 'k': case 'K': - viewer_move_mouse(viewer, 0, 1); + geo_move_mouse(&v->geo, viewer_geo(v), 0, 1); + viewer_vnc_send_pointer(v); break; case 'l': case 'L': - viewer_move_mouse(viewer, 1, 0); + geo_move_mouse(&v->geo, viewer_geo(v), 1, 0); + viewer_vnc_send_pointer(v); break; case 'u': case 'U': - viewer->mouse_left = true; - viewer_vnc_send_pointer(viewer); - viewer->mouse_left = false; - viewer_vnc_send_pointer(viewer); + v->mouse_left = true; + viewer_vnc_send_pointer(v); + v->mouse_left = false; + viewer_vnc_send_pointer(v); break; case 'o': case 'O': - viewer->mouse_right = true; - viewer_vnc_send_pointer(viewer); - viewer->mouse_right = false; - viewer_vnc_send_pointer(viewer); + v->mouse_right = true; + viewer_vnc_send_pointer(v); + v->mouse_right = false; + viewer_vnc_send_pointer(v); break; case '7': - viewer->mouse_left = !viewer->mouse_left; - viewer_vnc_send_pointer(viewer); + v->mouse_left = !v->mouse_left; + viewer_vnc_send_pointer(v); break; case '8': - viewer->mouse_middle = !viewer->mouse_middle; - viewer_vnc_send_pointer(viewer); + v->mouse_middle = !v->mouse_middle; + viewer_vnc_send_pointer(v); break; case '9': - viewer->mouse_right = !viewer->mouse_right; - viewer_vnc_send_pointer(viewer); + v->mouse_right = !v->mouse_right; + viewer_vnc_send_pointer(v); break; case '0': - viewer->mouse_middle = true; - viewer_vnc_send_pointer(viewer); - viewer->mouse_middle = false; - viewer_vnc_send_pointer(viewer); + v->mouse_middle = true; + viewer_vnc_send_pointer(v); + v->mouse_middle = false; + viewer_vnc_send_pointer(v); break; case 'p': case 'P': - viewer_zoom_to_cursor(viewer); + geo_zoom_to_cursor(&v->geo, viewer_geo(v)); break; /* Toggle keys */ case 'z': case 'Z': - viewer->hold_lctrl = !viewer->hold_lctrl; - viewer_vnc_toggle_key(viewer, XK_Control_L, viewer->hold_lctrl); + v->hold_lctrl = !v->hold_lctrl; + viewer_vnc_toggle_key(v, XK_Control_L, v->hold_lctrl); break; case 'm': case 'M': - viewer->hold_rctrl = !viewer->hold_rctrl; - viewer_vnc_toggle_key(viewer, XK_Control_R, viewer->hold_rctrl); + v->hold_rctrl = !v->hold_rctrl; + viewer_vnc_toggle_key(v, XK_Control_R, v->hold_rctrl); break; case 'x': case 'X': - viewer->hold_lshift = !viewer->hold_lshift; - viewer_vnc_toggle_key(viewer, XK_Shift_L, viewer->hold_lshift); + v->hold_lshift = !v->hold_lshift; + viewer_vnc_toggle_key(v, XK_Shift_L, v->hold_lshift); break; case 'n': case 'N': - viewer->hold_rshift = !viewer->hold_rshift; - viewer_vnc_toggle_key(viewer, XK_Shift_R, viewer->hold_rshift); + v->hold_rshift = !v->hold_rshift; + viewer_vnc_toggle_key(v, XK_Shift_R, v->hold_rshift); break; case 'c': case 'C': - viewer->hold_lalt = !viewer->hold_lalt; - viewer_vnc_toggle_key(viewer, XK_Alt_L, viewer->hold_lalt); + v->hold_lalt = !v->hold_lalt; + viewer_vnc_toggle_key(v, XK_Alt_L, v->hold_lalt); break; case 'b': case 'B': - viewer->hold_ralt = !viewer->hold_ralt; - viewer_vnc_toggle_key(viewer, XK_Alt_R, viewer->hold_ralt); + v->hold_ralt = !v->hold_ralt; + viewer_vnc_toggle_key(v, XK_Alt_R, v->hold_ralt); break; case 'v': case 'V': - viewer->hold_lsuper = !viewer->hold_lsuper; - viewer_vnc_toggle_key(viewer, XK_Super_L, viewer->hold_lsuper); + v->hold_lsuper = !v->hold_lsuper; + viewer_vnc_toggle_key(v, XK_Super_L, v->hold_lsuper); break; } + v->last_viewer_control = get_time_usec(); + return true; } -void viewer_terminate(struct viewer *viewer) +void viewer_terminate(struct viewer *v) { - caca_free_display(viewer->disp); - caca_free_canvas(viewer->view); - if (viewer->fb_dither != NULL) { - caca_free_dither(viewer->fb_dither); + if (v->fb_dither != NULL) { + caca_free_dither(v->fb_dither); + } + if (v->disp != NULL) { + caca_free_display(v->disp); + } + if (v->view != NULL) { + caca_free_canvas(v->view); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/headmore-1.0/viewer.h new/headmore-1.1.1/viewer.h --- old/headmore-1.0/viewer.h 2016-10-18 09:22:01.000000000 +0200 +++ new/headmore-1.1.1/viewer.h 2016-11-02 09:26:31.000000000 +0100 @@ -1,68 +1,64 @@ #ifndef VIEWER_H #define VIEWER_H +#include <caca.h> #include <stdbool.h> #include <sys/types.h> -#include <caca.h> +#include "geo.h" #include "vnc.h" -#define VIEWER_PAN_STEP 0.20 -#define VIEWER_ZOOM_STEP 1.20f -#define VIEWER_ZOOM_MAX_LVL 15 -#define VIEWER_FPS 10 /* too high FPS will make control sluggish, never go above 50. */ +/* + * The viewer will render frame-buffer content at roughly this many frames per second. + * It does not take rendering algorithm itself into account, hence the value is a rough estimate at best. + * The perceived FPS decreases as terminal gets larger. + * Do not raise the value too high or controls will become very sluggish. + */ +#define VIEWER_FPS 10 +#define VIEWER_FRAME_INTVL_USEC (1000000 / VIEWER_FPS) +#define VIEWER_MAX_INPUT_INTVL_USEC (1000000 / VIEWER_FPS) -/* Draw remote frame-buffer and handle key/mouse IO. */ +/* Render remote frame-buffer on terminal and handle key/mouse IO. */ struct viewer { struct vnc *vnc; - caca_display_t *disp; + struct geo geo; + caca_display_t *disp; caca_canvas_t *view; struct caca_dither *fb_dither; - float view_x, zoom_x, view_y, zoom_y; - int zoom_lvl; - float zoom_lvls[VIEWER_ZOOM_MAX_LVL + 1]; - int mouse_speed[VIEWER_ZOOM_MAX_LVL + 1]; - int mouse_x, mouse_y; - bool mouse_left, mouse_middle, mouse_right; - - suseconds_t last_vnc_esc, last_viewer_esc; + suseconds_t last_vnc_esc, last_viewer_control; bool void_backsp, void_tab, void_ret, void_pause, void_esc, void_del; bool disp_help, input2vnc; bool hold_lctrl, hold_lshift, hold_lalt, hold_lsuper, hold_ralt, hold_rshift, hold_rctrl; + bool mouse_left, mouse_middle, mouse_right; }; -/* Initialise canvas and its driver for the VNC connection. */ -bool viewer_init(struct viewer *viewer, struct vnc *vnc); +/* Initialise viewer and its driver for the VNC connection. */ +bool viewer_init(struct viewer *v, struct vnc *vnc); +/* Return geometry facts of the viewer. */ +struct geo_facts viewer_geo(struct viewer *v); /* Display a status row at 0,0. */ -void viewer_disp_status(struct viewer *viewer); +void viewer_disp_status(struct viewer *v); /* Display a static help menu at 0,1. */ -void viewer_disp_help(struct viewer *viewer); -/* Zoom in (+) or out (-) on the canvas. Remember to redraw! */ -void viewer_zoom(struct viewer *viewer, int offset); -/* Pan the canvas several steps up (-y), down (+y), left (-x), or right (+x). Remember to redraw! */ -void viewer_pan(struct viewer *viewer, int pan_x, int pan_y); +void viewer_disp_help(struct viewer *v); /* Redraw the content from the latest frame-buffer of VNC connection. */ -void viewer_redraw(struct viewer *viewer); +void viewer_redraw(struct viewer *v); /* Handle keyboard input and canvas events. Block caller until quit key is pressed and handled. */ -void viewer_ev_loop(struct viewer *viewer); +void viewer_ev_loop(struct viewer *v); /* Click (press and release) a keyboard key in VNC. */ -void viewer_vnc_click_key(struct viewer *viewer, int vnc_key); +void viewer_vnc_click_key(struct viewer *v, int vnc_key); /* Hold control key and then click the specified key in VNC, then release control key. */ -void viewer_vnc_click_ctrl_key_combo(struct viewer *viewer, int vnc_key); +void viewer_vnc_click_ctrl_key_combo(struct viewer *v, int vnc_key); /* Toggle (press or release) a keyboard key in VNC. */ -void viewer_vnc_toggle_key(struct viewer *viewer, int vnc_key, bool key_down); +void viewer_vnc_toggle_key(struct viewer *v, int vnc_key, bool key_down); /* Send the latest pointer location and button mask to VNC. */ -void viewer_vnc_send_pointer(struct viewer *viewer); -/* Move mouse pointer several steps up (-y) down (+y), left (-x) or right (+x).*/ -void viewer_move_mouse(struct viewer *viewer, int step_x, int step_y); -/* Move view to the location of mouse cursor and zoom in there to a pre-defined level. */ -void viewer_zoom_to_cursor(struct viewer *viewer); +void viewer_vnc_send_pointer(struct viewer *v); /* Translate caca key stroke to VNC key symbol and send it over VNC. */ -void viewer_input_to_vnc(struct viewer *viewer, int caca_key); -/* Interpret and act on caca key stroke as a viewer control command. */ -void viewer_handle_control(struct viewer *viewer, int caca_key); +void viewer_input_to_vnc(struct viewer *v, int caca_key); +/* Interpret and act on caca key stroke as a viewer control command. Return false only if viewer should quit. */ +bool viewer_handle_control(struct viewer *v, int caca_key); /* Release all resources held by the viewer, but do not terminate the VNC connection. */ -void viewer_terminate(struct viewer *viewer); +void viewer_terminate(struct viewer *v); + #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/headmore-1.0/vnc.c new/headmore-1.1.1/vnc.c --- old/headmore-1.0/vnc.c 2016-10-18 09:22:01.000000000 +0200 +++ new/headmore-1.1.1/vnc.c 2016-11-02 09:26:31.000000000 +0100 @@ -1,8 +1,6 @@ -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <time.h> #include <unistd.h> #include <sys/types.h> #include <rfb/keysym.h> @@ -11,26 +9,6 @@ #include <caca.h> #include "vnc.h" -static FILE *vnc_dbg_log = NULL; /* direct VNC library log to a tmp file */ - -/* Write a log line into log file. */ -static void vnc_log2file(const char *format, ...) -{ - if (!rfbEnableClientLogging || vnc_dbg_log == NULL) { - return; - } - time_t now; - time(&now); - char timestamp[100]; - strftime(timestamp, 100 - 1, "%Y-%m-%d %X ", localtime(&now)); - fprintf(vnc_dbg_log, "%s", timestamp); - va_list args; - va_start(args, format); - vfprintf(vnc_dbg_log, format, args); - va_end(args); - fflush(vnc_dbg_log); -} - /* Process RFB server messages, block caller. Return NULL. */ static void *io_loop_fun(void *struct_vnc) { @@ -50,60 +28,38 @@ return NULL; } -bool vnc_init(struct vnc * vnc, int argc, char **argv) +bool vnc_init(struct vnc * v, int argc, char **argv) { - memset(vnc, 0, sizeof(struct vnc)); - /* A debug log file is created under user's home directory */ - char *home_dir_path = getenv("HOME"); - if (home_dir_path != NULL) { - char *log_file_path = - (char *)calloc(strlen(home_dir_path) + strlen(VNC_DBG_LOG) + - 2, sizeof(char)); - if (!log_file_path) { - fprintf(stderr, "Out of memory"); - return false; - } - strcat(log_file_path, home_dir_path); - strcat(log_file_path, "/"); - strcat(log_file_path, VNC_DBG_LOG); - printf("Log file is located at %s\n", log_file_path); - vnc_dbg_log = fopen(log_file_path, "a"); - free(log_file_path); - } - /* libvncserver shares logging functions across all connections */ - rfbClientLog = vnc_log2file; - rfbClientErr = vnc_log2file; + memset(v, 0, sizeof(struct vnc)); /* * The connection asks server for 32-bit RGB colours. * Take note that VNC does not use alpha channel, hence the most significant 2 bytes are useless. * I think 256-colours should be more than sufficient for this VNC client, but somehow I could not * get a sufficiently good quality out of dithering process, perhaps I got the bit-masks wrong? */ - vnc->conn = rfbGetClient(8, 3, 4); - vnc->conn->canHandleNewFBSize = FALSE; - printf("Establishing VNC connection...\n"); - if (!rfbInitClient(vnc->conn, &argc, argv)) { + v->conn = rfbGetClient(8, 3, 4); + v->conn->canHandleNewFBSize = FALSE; + if (!rfbInitClient(v->conn, &argc, argv)) { return false; } - printf("Connection is successfully established!\n"); - vnc->cont_io_loop = true; - vnc->connected = true; - if (pthread_create(&vnc->io_loop, NULL, io_loop_fun, (void *)vnc) != 0) { + v->cont_io_loop = true; + v->connected = true; + if (pthread_create(&v->io_loop, NULL, io_loop_fun, (void *)v) != 0) { fprintf(stderr, "Failed to create message loop thread\n"); return false; } return true; } -void vnc_destroy(struct vnc *vnc) +void vnc_destroy(struct vnc *v) { - vnc->cont_io_loop = false; - vnc->connected = false; - if (pthread_join(vnc->io_loop, NULL) != 0) { + v->cont_io_loop = false; + v->connected = false; + if (pthread_join(v->io_loop, NULL) != 0) { fprintf(stderr, "Failed to join message loop thread\n"); } - uint8_t *fb = vnc->conn->frameBuffer; - rfbClientCleanup(vnc->conn); + uint8_t *fb = v->conn->frameBuffer; + rfbClientCleanup(v->conn); free(fb); rfbClientLog("VNC connection has been terminated\n"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/headmore-1.0/vnc.h new/headmore-1.1.1/vnc.h --- old/headmore-1.0/vnc.h 2016-10-18 09:22:01.000000000 +0200 +++ new/headmore-1.1.1/vnc.h 2016-11-02 09:26:31.000000000 +0100 @@ -4,9 +4,7 @@ #include <stdbool.h> #include <rfb/rfbclient.h> -#define VNC_POLL_TIMEOUT_USEC 100000 /* keep it under a second */ -#define VNC_DBG_LOG ".headmore.log" /* name of log file under user's home directory */ -#define VNC_PASS_MAX 256 /* maximum length of input password */ +#define VNC_POLL_TIMEOUT_USEC 100000 /* A lower value enables faster termination of VNC IO loop */ /* Connect to remote frame-buffer and handle control/image IO. */ struct vnc { @@ -16,9 +14,9 @@ }; /* Connect to server and immediately begin message loop in a separate thread. Return false only on failure. */ -bool vnc_init(struct vnc *vnc, int argc, char **argv); +bool vnc_init(struct vnc *v, int argc, char **argv); /* Close VNC connection and free all resources, including the VNC client itself. */ -void vnc_destroy(struct vnc *vnc); +void vnc_destroy(struct vnc *v); /* Translate a key code as read by libcaca to its corresponding VNC key code. Return -1 only if no translation. */ int cacakey2vnc(int keych);