From aae3891bc7380c23322214b9e2781b636c3d7199 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 13 Jul 2012 11:22:55 +0000 Subject: [PATCH] Implement client side multi-head support. Requires a FLTK patched to support fullscreen over multiple monitors. Will properly report screen configuration to the server, provided the server supports it. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4935 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- CMakeLists.txt | 3 + config.h.in | 1 + vncviewer/DesktopWindow.cxx | 155 +++++++++++++++++++++++++++++++----- vncviewer/DesktopWindow.h | 2 + vncviewer/OptionsDialog.cxx | 16 ++++ vncviewer/OptionsDialog.h | 1 + vncviewer/Viewport.cxx | 2 +- vncviewer/parameters.cxx | 5 ++ vncviewer/parameters.h | 3 + 9 files changed, 167 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cf3dd32..c375314a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,6 +289,9 @@ if(FLTK_FOUND) # FLTK STR #2816 check_cxx_source_compiles("#include \nint main(int c, char** v) { Fl_Window::default_icons(0, 0); return 0; }" HAVE_FLTK_ICONS) + # FLTK STR #2860 + check_cxx_source_compiles("#include \nint main(int c, char** v) { void (Fl_Window::*foo)(int,int,int,int) = &Fl_Window::fullscreen_screens; return 0; }" HAVE_FLTK_FULLSCREEN_SCREENS) + set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LIBRARIES) endif() diff --git a/config.h.in b/config.h.in index a11ec10b..55cd652d 100644 --- a/config.h.in +++ b/config.h.in @@ -19,6 +19,7 @@ #cmakedefine HAVE_FLTK_CLIPBOARD #cmakedefine HAVE_FLTK_MEDIAKEYS #cmakedefine HAVE_FLTK_FULLSCREEN +#cmakedefine HAVE_FLTK_FULLSCREEN_SCREENS #cmakedefine HAVE_FLTK_CURSOR #cmakedefine HAVE_FLTK_ICONS #cmakedefine HAVE_ACTIVE_DESKTOP_H diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 29053916..78a94b60 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -76,7 +76,7 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name, #ifdef HAVE_FLTK_FULLSCREEN if (fullScreen) - fullscreen(); + fullscreen_on(); else #endif { @@ -295,6 +295,54 @@ int DesktopWindow::fltkHandle(int event, Fl_Window *win) } +void DesktopWindow::fullscreen_on() +{ +#ifdef HAVE_FLTK_FULLSCREEN +#ifdef HAVE_FLTK_FULLSCREEN_SCREENS + if (not fullScreenAllMonitors) + fullscreen_screens(-1, -1, -1, -1); + else { + int top, bottom, left, right; + int top_y, bottom_y, left_x, right_x; + + int sx, sy, sw, sh; + + top = bottom = left = right = 0; + + Fl::screen_xywh(sx, sy, sw, sh, 0); + top_y = sy; + bottom_y = sy + sh; + left_x = sx; + right_x = sx + sw; + + for (int i = 1;i < Fl::screen_count();i++) { + Fl::screen_xywh(sx, sy, sw, sh, i); + if (sy < top_y) { + top = i; + top_y = sy; + } + if ((sy + sh) > bottom_y) { + bottom = i; + bottom_y = sy + sh; + } + if (sx < left_x) { + left = i; + left_x = sx; + } + if ((sx + sw) > right_x) { + right = i; + right_x = sx + sw; + } + } + + fullscreen_screens(top, bottom, left, right); + } +#endif // HAVE_FLTK_FULLSCREEN_SCREENS + + fullscreen(); +#endif // HAVE_FLTK_FULLSCREEN +} + void DesktopWindow::grabKeyboard() { // Grabbing the keyboard is fairly safe as FLTK reroutes events to the @@ -394,6 +442,7 @@ void DesktopWindow::remoteResize() { int width, height; ScreenSet layout; + ScreenSet::iterator iter; if (firstUpdate) { if (sscanf(desktopSize.getValueStr(), "%dx%d", &width, &height) != 2) @@ -403,28 +452,88 @@ void DesktopWindow::remoteResize() height = h(); } - layout = cc->cp.screenLayout; +#ifdef HAVE_FLTK_FULLSCREEN + if (!fullscreen_active()) { +#endif + // In windowed mode we just report a single virtual screen that + // covers the entire framebuffer. + + layout = cc->cp.screenLayout; - if (layout.num_screens() == 0) - layout.add_screen(rfb::Screen()); - else if (layout.num_screens() != 1) { - ScreenSet::iterator iter; + // Not sure why we have no screens, but adding a new one should be + // safe as there is nothing to conflict with... + if (layout.num_screens() == 0) + layout.add_screen(rfb::Screen()); + else if (layout.num_screens() != 1) { + // More than one screen. Remove all but the first (which we + // assume is the "primary"). - while (true) { - iter = layout.begin(); - ++iter; + while (true) { + iter = layout.begin(); + ++iter; - if (iter == layout.end()) - break; + if (iter == layout.end()) + break; - layout.remove_screen(iter->id); + layout.remove_screen(iter->id); + } } - } - layout.begin()->dimensions.tl.x = 0; - layout.begin()->dimensions.tl.y = 0; - layout.begin()->dimensions.br.x = width; - layout.begin()->dimensions.br.y = height; + // Resize the remaining single screen to the complete framebuffer + layout.begin()->dimensions.tl.x = 0; + layout.begin()->dimensions.tl.y = 0; + layout.begin()->dimensions.br.x = width; + layout.begin()->dimensions.br.y = height; +#ifdef HAVE_FLTK_FULLSCREEN + } else { + int i; + rdr::U32 id; + int sx, sy, sw, sh; + + // In full screen we report all screens that are fully covered. + + // If we can find a matching screen in the existing set, we use + // that, otherwise we create a brand new screen. + // + // FIXME: We should really track screens better so we can handle + // a resized one. + // + for (i = 0;i < Fl::screen_count();i++) { + Fl::screen_xywh(sx, sy, sw, sh, i); + + // Look for perfectly matching existing screen... + for (iter = cc->cp.screenLayout.begin(); + iter != cc->cp.screenLayout.end(); ++iter) { + if ((iter->dimensions.tl.x == sx) && + (iter->dimensions.tl.y == sy) && + (iter->dimensions.width() == sw) && + (iter->dimensions.height() == sh)) + break; + } + + // Found it? + if (iter != cc->cp.screenLayout.end()) { + layout.add_screen(*iter); + continue; + } + + // Need to add a new one, which means we need to find an unused id + while (true) { + id = rand(); + for (iter = cc->cp.screenLayout.begin(); + iter != cc->cp.screenLayout.end(); ++iter) { + if (iter->id == id) + break; + } + + if (iter == cc->cp.screenLayout.end()) + break; + } + + layout.add_screen(rfb::Screen(id, sx, sy, sw, sh, 0)); + } + } +#endif // Do we actually change anything? if ((width == cc->cp.width) && @@ -432,8 +541,14 @@ void DesktopWindow::remoteResize() (layout == cc->cp.screenLayout)) return; - vlog.debug("Requesting framebuffer resize from %dx%d to %dx%d", - cc->cp.width, cc->cp.height, width, height); + vlog.debug("Requesting framebuffer resize from %dx%d to %dx%d (%d screens)", + cc->cp.width, cc->cp.height, width, height, layout.num_screens()); + + if (!layout.validate(width, height)) { + vlog.error("Invalid screen layout computed for resize request!"); + layout.debug_print(); + return; + } cc->writer()->writeSetDesktopSize(width, height, layout); } @@ -499,7 +614,7 @@ void DesktopWindow::handleOptions(void *data) self->ungrabKeyboard(); if (fullScreen && !self->fullscreen_active()) - self->fullscreen(); + self->fullscreen_on(); else if (!fullScreen && self->fullscreen_active()) self->fullscreen_off(); #endif diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h index 3fe9c866..2a4613c7 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -78,6 +78,8 @@ public: int handle(int event); + void fullscreen_on(); + private: static int fltkHandle(int event, Fl_Window *win); diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx index c82079f5..df9f3554 100644 --- a/vncviewer/OptionsDialog.cxx +++ b/vncviewer/OptionsDialog.cxx @@ -288,6 +288,9 @@ void OptionsDialog::loadOptions(void) remoteResizeCheckbox->value(remoteResize); #ifdef HAVE_FLTK_FULLSCREEN fullScreenCheckbox->value(fullScreen); +#ifdef HAVE_FLTK_FULLSCREEN_SCREENS + fullScreenAllMonitorsCheckbox->value(fullScreenAllMonitors); +#endif // HAVE_FLTK_FULLSCREEN_SCREENS #endif // HAVE_FLTK_FULLSCREEN handleDesktopSize(desktopSizeCheckbox, this); @@ -393,6 +396,9 @@ void OptionsDialog::storeOptions(void) remoteResize.setParam(remoteResizeCheckbox->value()); #ifdef HAVE_FLTK_FULLSCREEN fullScreen.setParam(fullScreenCheckbox->value()); +#ifdef HAVE_FLTK_FULLSCREEN_SCREENS + fullScreenAllMonitors.setParam(fullScreenAllMonitorsCheckbox->value()); +#endif // HAVE_FLTK_FULLSCREEN_SCREENS #endif // HAVE_FLTK_FULLSCREEN /* Misc. */ @@ -760,6 +766,16 @@ void OptionsDialog::createScreenPage(int tx, int ty, int tw, int th) _("Full-screen mode"))); ty += CHECK_HEIGHT + TIGHT_MARGIN; +#ifdef HAVE_FLTK_FULLSCREEN_SCREENS + + fullScreenAllMonitorsCheckbox = new Fl_Check_Button(LBLRIGHT(tx + INDENT, ty, + CHECK_MIN_WIDTH, + CHECK_HEIGHT, + _("Enable full-screen mode over all monitors"))); + ty += CHECK_HEIGHT + TIGHT_MARGIN; + +#endif // HAVE_FLTK_FULLSCREEN_SCREENS + #endif // HAVE_FLTK_FULLSCREEN group->end(); diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h index c26c0c91..b3ac0156 100644 --- a/vncviewer/OptionsDialog.h +++ b/vncviewer/OptionsDialog.h @@ -114,6 +114,7 @@ protected: Fl_Int_Input *desktopHeightInput; Fl_Check_Button *remoteResizeCheckbox; Fl_Check_Button *fullScreenCheckbox; + Fl_Check_Button *fullScreenAllMonitorsCheckbox; /* Misc. */ Fl_Check_Button *sharedCheckbox; diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index a80b8efe..43b39d4f 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -920,7 +920,7 @@ void Viewport::popupContextMenu() if (window()->fullscreen_active()) window()->fullscreen_off(); else - window()->fullscreen(); + ((DesktopWindow*)window())->fullscreen_on(); break; #endif case ID_RESIZE: diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx index 4407bd95..22fa1ac5 100644 --- a/vncviewer/parameters.cxx +++ b/vncviewer/parameters.cxx @@ -66,6 +66,11 @@ IntParameter qualityLevel("QualityLevel", #ifdef HAVE_FLTK_FULLSCREEN BoolParameter fullScreen("FullScreen", "Full screen mode", false); +#ifdef HAVE_FLTK_FULLSCREEN_SCREENS +BoolParameter fullScreenAllMonitors("FullScreenAllMonitors", + "Enable full screen over all monitors", + true); +#endif // HAVE_FLTK_FULLSCREEN_SCREENS #endif // HAVE_FLTK_FULLSCREEN StringParameter desktopSize("DesktopSize", "Reconfigure desktop size on the server on " diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h index a935ea0e..59909d11 100644 --- a/vncviewer/parameters.h +++ b/vncviewer/parameters.h @@ -39,6 +39,9 @@ extern rfb::IntParameter qualityLevel; #ifdef HAVE_FLTK_FULLSCREEN extern rfb::BoolParameter fullScreen; +#ifdef HAVE_FLTK_FULLSCREEN_SCREENS +extern rfb::BoolParameter fullScreenAllMonitors; +#endif // HAVE_FLTK_FULLSCREEN_SCREENS #endif // HAVE_FLTK_FULLSCREEN extern rfb::StringParameter desktopSize; extern rfb::BoolParameter remoteResize; -- 2.39.5