From 0d43b96d1c2385fc8202462cf170d7ac7da5d2f7 Mon Sep 17 00:00:00 2001 From: Hugo Lundin Date: Tue, 13 Jul 2021 13:58:12 +0200 Subject: [PATCH] Add fullscreen mode for selected monitors The user might not always want to use all monitors when in fullscreen mode, but instead only a few. This commit adds support for configuring selected monitors from command line, in the config file and graphically in the options menu. Because it might be hard to guarantee the consistency of monitor identifiers coming from third-parties (for example FLTK), it has been decided to use our own numerical identifier. This identifier is based on the monitor's positions. The mapping between this identifier and the indices used by FLTK is done by MonitorIndicesParameter. --- vncviewer/CMakeLists.txt | 2 + vncviewer/DesktopWindow.cxx | 46 +++- vncviewer/MonitorArrangement.cxx | 303 ++++++++++++++++++++++++++ vncviewer/MonitorArrangement.h | 72 ++++++ vncviewer/MonitorIndicesParameter.cxx | 229 +++++++++++++++++++ vncviewer/MonitorIndicesParameter.h | 44 ++++ vncviewer/OptionsDialog.cxx | 44 ++++ vncviewer/OptionsDialog.h | 5 + vncviewer/parameters.cxx | 7 +- vncviewer/parameters.h | 2 + vncviewer/vncviewer.man | 10 +- 11 files changed, 753 insertions(+), 11 deletions(-) create mode 100644 vncviewer/MonitorArrangement.cxx create mode 100644 vncviewer/MonitorArrangement.h create mode 100644 vncviewer/MonitorIndicesParameter.cxx create mode 100644 vncviewer/MonitorIndicesParameter.h diff --git a/vncviewer/CMakeLists.txt b/vncviewer/CMakeLists.txt index e4fad782..9d11ba79 100644 --- a/vncviewer/CMakeLists.txt +++ b/vncviewer/CMakeLists.txt @@ -17,6 +17,8 @@ set(VNCVIEWER_SOURCES parameters.cxx keysym2ucs.c touch.cxx + MonitorArrangement.cxx + MonitorIndicesParameter.cxx vncviewer.cxx) if(WIN32) diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 1fdce182..0b957d39 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -883,8 +883,9 @@ int DesktopWindow::fltkHandle(int event, Fl_Window *win) void DesktopWindow::fullscreen_on() { bool all_monitors = !strcasecmp(fullScreenMode, "all"); + bool selected_monitors = !strcasecmp(fullScreenMode, "selected"); - if (not all_monitors) + if (not selected_monitors and not all_monitors) fullscreen_screens(-1, -1, -1, -1); else { int top, bottom, left, right; @@ -892,30 +893,57 @@ void DesktopWindow::fullscreen_on() int sx, sy, sw, sh; - top = bottom = left = right = 0; + std::set monitors; - Fl::screen_xywh(sx, sy, sw, sh, 0); + if (selected_monitors and not all_monitors) { + std::set selected = fullScreenSelectedMonitors.getParam(); + monitors.insert(selected.begin(), selected.end()); + } else { + for (int i = 0; i < Fl::screen_count(); i++) { + monitors.insert(i); + } + } + + // If no monitors were found in the selected monitors case, we want + // to explicitly use the window's current monitor. + if (monitors.size() == 0) { + monitors.insert(Fl::screen_num(x(), y(), w(), h())); + } + + // If there are monitors selected, calculate the dimensions + // of the frame buffer, expressed in the monitor indices that + // limits it. + std::set::iterator it = monitors.begin(); + + // Get first monitor dimensions. + Fl::screen_xywh(sx, sy, sw, sh, *it); + top = bottom = left = right = *it; 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); + // Keep going through the rest of the monitors. + for (; it != monitors.end(); it++) { + Fl::screen_xywh(sx, sy, sw, sh, *it); + if (sy < top_y) { - top = i; + top = *it; top_y = sy; } + if ((sy + sh) > bottom_y) { - bottom = i; + bottom = *it; bottom_y = sy + sh; } + if (sx < left_x) { - left = i; + left = *it; left_x = sx; } + if ((sx + sw) > right_x) { - right = i; + right = *it; right_x = sx + sw; } } diff --git a/vncviewer/MonitorArrangement.cxx b/vncviewer/MonitorArrangement.cxx new file mode 100644 index 00000000..e5ff3652 --- /dev/null +++ b/vncviewer/MonitorArrangement.cxx @@ -0,0 +1,303 @@ +/* Copyright 2021 Hugo Lundin for Cendio AB. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "MonitorArrangement.h" + +static const Fl_Boxtype FL_CHECKERED_BOX = FL_FREE_BOXTYPE; + +MonitorArrangement::MonitorArrangement( + int x, int y, int w, int h) +: Fl_Group(x, y, w, h), + SELECTION_COLOR(fl_lighter(FL_BLUE)), + AVAILABLE_COLOR(fl_lighter(fl_lighter(fl_lighter(FL_BACKGROUND_COLOR)))), + m_monitors() +{ + // Used for required monitors. + Fl::set_boxtype(FL_CHECKERED_BOX, checkered_pattern_draw, 0, 0, 0, 0); + + box(FL_DOWN_BOX); + color(fl_lighter(FL_BACKGROUND_COLOR)); + layout(); + end(); +} + +MonitorArrangement::~MonitorArrangement() +{ + +} + +std::set MonitorArrangement::get() +{ + std::set indices; + + for (int i = 0; i < (int) m_monitors.size(); i++) { + if (m_monitors[i]->value() == 1) + indices.insert(i); + } + + return indices; +} + +void MonitorArrangement::set(std::set indices) +{ + for (int i = 0; i < (int) m_monitors.size(); i++) { + bool selected = std::find(indices.begin(), indices.end(), i) != indices.end(); + m_monitors[i]->value(selected ? 1 : 0); + } +} + +void MonitorArrangement::draw() +{ + for (int i = 0; i < (int) m_monitors.size(); i++) { + Fl_Button * monitor = m_monitors[i]; + + if (is_required(i)) { + monitor->box(FL_CHECKERED_BOX); + monitor->color(SELECTION_COLOR); + } else { + monitor->box(FL_BORDER_BOX); + monitor->color(AVAILABLE_COLOR); + monitor->selection_color(SELECTION_COLOR); + } + } + + Fl_Group::draw(); +} + +void MonitorArrangement::layout() +{ + int x, y, w, h; + double scale = this->scale(); + const double MARGIN_SCALE_FACTOR = 0.99; + std::pair offset = this->offset(); + + for (int i = 0; i < Fl::screen_count(); i++) { + Fl::screen_xywh(x, y, w, h, i); + + Fl_Button *monitor = new Fl_Button( + /* x = */ this->x() + offset.first + x*scale + (1 - MARGIN_SCALE_FACTOR)*x*scale, + /* y = */ this->y() + offset.second + y*scale + (1 - MARGIN_SCALE_FACTOR)*y*scale, + /* w = */ w*scale*MARGIN_SCALE_FACTOR, + /* h = */ h*scale*MARGIN_SCALE_FACTOR + ); + + monitor->clear_visible_focus(); + monitor->callback(monitor_pressed, this); + monitor->type(FL_TOGGLE_BUTTON); + monitor->when(FL_WHEN_CHANGED); + m_monitors.push_back(monitor); + } +} + +bool MonitorArrangement::is_required(int m) +{ + // A selected monitor is never required. + if (m_monitors[m]->value() == 1) + return false; + + // If no monitors are selected, none are required. + std::set selected = get(); + if (selected.size() <= 0) + return false; + + + // Go through all selected monitors and find the monitor + // indices that bounds the fullscreen frame buffer. If + // the given monitor's coordinates are inside the bounds, + // while not being selected, it is instead required. + + int x, y, w, h; + int top_y, bottom_y, left_x, right_x; + std::set::iterator it = selected.begin(); + + // Base the rest of the calculations on the dimensions + // obtained for the first monitor. + Fl::screen_xywh(x, y, w, h, *it); + top_y = y; + bottom_y = y + h; + left_x = x; + right_x = x + w; + + // Go through the rest of the monitors, + // exhausting the rest of the iterator. + for (; it != selected.end(); it++) { + Fl::screen_xywh(x, y, w, h, *it); + + if (y < top_y) { + top_y = y; + } + + if ((y + h) > bottom_y) { + bottom_y = y + h; + } + + if (x < left_x) { + left_x = x; + } + + if ((x + w) > right_x) { + right_x = x + w; + } + } + + rfb::Rect viewport, monitor; + viewport.setXYWH(left_x, top_y, right_x - left_x, bottom_y - top_y); + + Fl::screen_xywh(x, y, w, h, m); + monitor.setXYWH(x, y, w, h); + + return monitor.enclosed_by(viewport); +} + +double MonitorArrangement::scale() +{ + const int MARGIN = 20; + std::pair size = this->size(); + + double s_w = static_cast(this->w()-MARGIN) / static_cast(size.first); + double s_h = static_cast(this->h()-MARGIN) / static_cast(size.second); + + // Choose the one that scales the least, in order to + // maximize our use of the given bounding area. + if (s_w > s_h) + return s_h; + else + return s_w; +} + +std::pair MonitorArrangement::size() +{ + int x, y, w, h; + int top, bottom, left, right; + int x_min, x_max, y_min, y_max; + x_min = x_max = y_min = y_max = 0; + + for (int i = 0; i < Fl::screen_count(); i++) { + Fl::screen_xywh(x, y, w, h, i); + + top = y; + bottom = y + h; + left = x; + right = x + w; + + if (top < y_min) + y_min = top; + + if (bottom > y_max) + y_max = bottom; + + if (left < x_min) + x_min = left; + + if (right > x_max) + x_max = right; + } + + return std::make_pair(x_max - x_min, y_max - y_min); +} + +std::pair MonitorArrangement::offset() +{ + double scale = this->scale(); + std::pair size = this->size(); + std::pair origin = this->origin(); + + int offset_x = (this->w()/2) - (size.first/2 * scale); + int offset_y = (this->h()/2) - (size.second/2 * scale); + + return std::make_pair(offset_x + abs(origin.first)*scale, offset_y + abs(origin.second)*scale); +} + +std::pair MonitorArrangement::origin() +{ + int x, y, w, h, ox, oy; + ox = oy = 0; + + for (int i = 0; i < Fl::screen_count(); i++) { + Fl::screen_xywh(x, y, w, h, i); + + if (x < ox) + ox = x; + + if (y < oy) + oy = y; + } + + return std::make_pair(ox, oy); +} + +void MonitorArrangement::monitor_pressed(Fl_Widget *widget, void *user_data) +{ + MonitorArrangement *self = (MonitorArrangement *) user_data; + + // When a monitor is selected, FLTK changes the state of it for us. + // However, selecting a monitor might implicitly change the state of + // others (if they become required). FLTK only redraws the selected + // monitor. Therefore, we must trigger a redraw of the whole widget + // manually. + self->redraw(); +} + +void MonitorArrangement::checkered_pattern_draw( + int x, int y, int width, int height, Fl_Color color) +{ + bool draw_checker = false; + const int CHECKER_SIZE = 8; + + fl_color(fl_lighter(fl_lighter(fl_lighter(color)))); + fl_rectf(x, y, width, height); + + fl_color(Fl::draw_box_active() ? color : fl_inactive(color)); + + // Round up the square count. Later on, we remove square area that are + // outside the given bounding area. + const int count = (width + CHECKER_SIZE - 1) / CHECKER_SIZE; + + for (int i = 0; i < count; i++) { + for (int j = 0; j < count; j++) { + + draw_checker = (i + j) % 2 == 0; + + if (draw_checker) { + fl_rectf( + /* x = */ x + i * CHECKER_SIZE, + /* y = */ y + j * CHECKER_SIZE, + /* w = */ CHECKER_SIZE - std::max(0, ((i + 1) * CHECKER_SIZE) - width), + /* h = */ CHECKER_SIZE - std::max(0, ((j + 1) * CHECKER_SIZE) - height) + ); + } + } + } + + fl_color(Fl::draw_box_active() ? FL_BLACK : fl_inactive(FL_BLACK)); + fl_rect(x, y, width, height); +} diff --git a/vncviewer/MonitorArrangement.h b/vncviewer/MonitorArrangement.h new file mode 100644 index 00000000..c29b6ff6 --- /dev/null +++ b/vncviewer/MonitorArrangement.h @@ -0,0 +1,72 @@ +/* Copyright 2021 Hugo Lundin for Cendio AB. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __MONITOR_ARRANGEMENT_H__ +#define __MONITOR_ARRANGEMENT_H__ + +#include + +class Fl_Group; +class Fl_Button; + +class MonitorArrangement: public Fl_Group { +public: + MonitorArrangement(int x, int y, int w, int h); + ~MonitorArrangement(); + + // Get selected indices. + std::set get(); + + // Set selected indices. + void set(std::set indices); + +protected: + virtual void draw(); + +private: + const Fl_Color SELECTION_COLOR; + const Fl_Color AVAILABLE_COLOR; + std::vector m_monitors; + + // Layout the monitor arrangement. + void layout(); + + // Return true if the given monitor is required to be part of the configuration + // for it to be valid. A configuration is only valid if the framebuffer created + // from is rectangular. + bool is_required(int m); + + // Calculate the scale of the monitor arrangement. + double scale(); + + // Return the size of the monitor arrangement. + std::pair size(); + + // Return the offset required for centering the monitor + // arrangement in the given bounding area. + std::pair offset(); + + // Return the origin of the monitor arrangement (top left corner). + std::pair origin(); + + static void monitor_pressed(Fl_Widget *widget, void *user_data); + static void checkered_pattern_draw( + int x, int y, int width, int height, Fl_Color color); +}; + +#endif diff --git a/vncviewer/MonitorIndicesParameter.cxx b/vncviewer/MonitorIndicesParameter.cxx new file mode 100644 index 00000000..54764af7 --- /dev/null +++ b/vncviewer/MonitorIndicesParameter.cxx @@ -0,0 +1,229 @@ +/* Copyright 2021 Hugo Lundin for Cendio AB. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "i18n.h" +#include +#include + +#include "MonitorIndicesParameter.h" + +using namespace rfb; +static LogWriter vlog("MonitorIndicesParameter"); + +MonitorIndicesParameter::MonitorIndicesParameter(const char* name_, const char* desc_, const char* v) +: StringParameter(name_, desc_, v) {} + +std::set MonitorIndicesParameter::getParam() +{ + bool valid = false; + std::set indices; + std::set config_indices; + std::vector monitors = this->monitors(); + + if (monitors.size() <= 0) { + vlog.error(_("Failed to get monitors.")); + return indices; + } + + valid = parse_indices(value, &config_indices); + if (!valid) { + return indices; + } + + if (config_indices.size() <= 0) { + return indices; + } + + // Go through the monitors and see what indices are present in the config. + for (int i = 0; i < ((int) monitors.size()); i++) { + if (std::find(config_indices.begin(), config_indices.end(), i) != config_indices.end()) + indices.insert(monitors[i].fltk_index); + } + + return indices; +} + +bool MonitorIndicesParameter::setParam(const char* value) +{ + int index; + std::set indices; + + if (strlen(value) < 0) + return false; + + if (!parse_indices(value, &indices)) { + vlog.error(_("Invalid FullScreenSelectedMonitors configuration.")); + return false; + } + + for (std::set::iterator it = indices.begin(); it != indices.end(); it++) { + index = *it + 1; + + if (index <= 0 || index > Fl::screen_count()) + vlog.error(_("Monitor index %d does not exist."), index); + } + + return StringParameter::setParam(value); +} + +bool MonitorIndicesParameter::setParam(std::set indices) +{ + static const int BUF_MAX_LEN = 1024; + char buf[BUF_MAX_LEN] = {0}; + std::set config_indices; + std::vector monitors = this->monitors(); + + if (monitors.size() <= 0) { + vlog.error(_("Failed to get monitors.")); + // Don't return, store the configuration anyways. + } + + for (int i = 0; i < ((int) monitors.size()); i++) { + if (std::find(indices.begin(), indices.end(), monitors[i].fltk_index) != indices.end()) + config_indices.insert(i); + } + + int bytes_written = 0; + char const * separator = ""; + + for (std::set::iterator index = config_indices.begin(); + index != config_indices.end(); + index++) + { + bytes_written += snprintf( + buf+bytes_written, + BUF_MAX_LEN-bytes_written, + "%s%u", + separator, + (*index)+1 + ); + + separator = ","; + } + + return setParam(buf); +} + +static bool parse_number(std::string number, std::set *indices) +{ + if (number.size() <= 0) + return false; + + int v = strtol(number.c_str(), NULL, 0); + + if (v <= 0) { + vlog.error(_("The given monitor index(%s) is too small to be valid."), number.c_str()); + return false; + } + + if (v > INT_MAX) { + vlog.error(_("The given monitor index (%s) is too large to be valid."), number.c_str()); + return false; + } + + indices->insert(v-1); + return true; +} + +bool MonitorIndicesParameter::parse_indices(const char* value, std::set *indices) +{ + char d; + std::string current; + + for (size_t i = 0; i < strlen(value); i++) { + d = value[i]; + + if (d == ' ') + continue; + else if (d >= '0' && d <= '9') + current.push_back(d); + else if (d == ',') { + if (!parse_number(current, indices)) { + vlog.error(_("Invalid monitor index '%s' in FullScreenSelectedMonitors"), current.c_str()); + return false; + } + + current.clear(); + } else { + vlog.error(_("Unexpected character '%c' in FullScreenSelectedMonitors"), d); + return false; + } + } + + // If we have nothing left to parse we are in a valid state. + if (current.size() == 0) + return true; + + // Parsing anything we have left. + if (!parse_number(current, indices)) + return false; + + return true; +} + +std::vector MonitorIndicesParameter::monitors() +{ + std::vector monitors; + + // Start by creating a struct for every monitor. + for (int i = 0; i < Fl::screen_count(); i++) { + Monitor monitor = {0}; + + // Get the properties of the monitor at the current index; + Fl::screen_xywh( + monitor.x, + monitor.y, + monitor.w, + monitor.h, + i + ); + + monitor.fltk_index = i; + monitors.push_back(monitor); + } + + // Sort the monitors according to the specification in the vncviewer manual. + qsort(&monitors[0], monitors.size(), sizeof(*(&monitors[0])), sort_cb); + return monitors; +} + +int MonitorIndicesParameter::sort_cb(const void *a, const void *b) +{ + MonitorIndicesParameter::Monitor * monitor1 = (MonitorIndicesParameter::Monitor *) a; + MonitorIndicesParameter::Monitor * monitor2 = (MonitorIndicesParameter::Monitor *) b; + + if (monitor1->x < monitor2->x) + return -1; + + if (monitor1->y < monitor2->y) + return -1; + + if (monitor1->x == monitor2->x) + if (monitor1->y == monitor2->y) + return 0; + + return 1; +} diff --git a/vncviewer/MonitorIndicesParameter.h b/vncviewer/MonitorIndicesParameter.h new file mode 100644 index 00000000..1737a5b3 --- /dev/null +++ b/vncviewer/MonitorIndicesParameter.h @@ -0,0 +1,44 @@ +/* Copyright 2021 Hugo Lundin for Cendio AB. + * + * This 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 software 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 software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __MONITOR_INDEX_PARAMETER_H +#define __MONITOR_INDEX_PARAMETER_H + +#include +#include + +#include + +class MonitorIndicesParameter: public rfb::StringParameter { +public: + MonitorIndicesParameter(const char* name_, const char* desc_, const char* v); + std::set getParam(); + bool setParam(std::set indices); + bool setParam(const char* value); +private: + typedef struct { + int x, y, w, h; + int fltk_index; + } Monitor; + + bool parse_indices(const char* value, std::set *indices); + std::vector monitors(); + static int sort_cb(const void*, const void*); +}; + +#endif // __MONITOR_INDEX_PARAMETER_H diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx index cbf4571d..190d8aa3 100644 --- a/vncviewer/OptionsDialog.cxx +++ b/vncviewer/OptionsDialog.cxx @@ -38,6 +38,7 @@ #include "i18n.h" #include "menukey.h" #include "parameters.h" +#include "MonitorArrangement.h" #include #include @@ -300,11 +301,16 @@ void OptionsDialog::loadOptions(void) if (!strcasecmp(fullScreenMode, "all")) { allMonitorsButton->setonly(); + } else if (!strcasecmp(fullScreenMode, "selected")) { + selectedMonitorsButton->setonly(); } else { currentMonitorButton->setonly(); } + monitorArrangement->set(fullScreenSelectedMonitors.getParam()); + handleDesktopSize(desktopSizeCheckbox, this); + handleFullScreenMode(selectedMonitorsButton, this); /* Misc. */ sharedCheckbox->value(shared); @@ -415,10 +421,14 @@ void OptionsDialog::storeOptions(void) if (allMonitorsButton->value()) { fullScreenMode.setParam("All"); + } else if (selectedMonitorsButton->value()) { + fullScreenMode.setParam("Selected"); } else { fullScreenMode.setParam("Current"); } + fullScreenSelectedMonitors.setParam(monitorArrangement->get()); + /* Misc. */ shared.setParam(sharedCheckbox->value()); dotWhenNoCursor.setParam(dotCursorCheckbox->value()); @@ -812,6 +822,7 @@ void OptionsDialog::createScreenPage(int tx, int ty, int tw, int th) RADIO_HEIGHT, _("Use current monitor"))); currentMonitorButton->type(FL_RADIO_BUTTON); + currentMonitorButton->callback(handleFullScreenMode, this); ty += RADIO_HEIGHT + TIGHT_MARGIN; allMonitorsButton = new Fl_Round_Button(LBLRIGHT(tx, ty, @@ -819,7 +830,29 @@ void OptionsDialog::createScreenPage(int tx, int ty, int tw, int th) RADIO_HEIGHT, _("Use all monitors"))); allMonitorsButton->type(FL_RADIO_BUTTON); + allMonitorsButton->callback(handleFullScreenMode, this); + ty += RADIO_HEIGHT + TIGHT_MARGIN; + + selectedMonitorsButton = new Fl_Round_Button(LBLRIGHT(tx, ty, + RADIO_MIN_WIDTH, + RADIO_HEIGHT, + _("Use selected monitor(s)"))); + selectedMonitorsButton->type(FL_RADIO_BUTTON); + selectedMonitorsButton->callback(handleFullScreenMode, this); ty += RADIO_HEIGHT + TIGHT_MARGIN; + + int full_width = tw - OUTER_MARGIN * 2; + int margin_width = full_width - INDENT - INNER_MARGIN*2; + int full_height = th; + int margin_height = full_height - ty + INNER_MARGIN*3; + + monitorArrangement = new MonitorArrangement( + tx + INDENT, + ty, + margin_width, + margin_height); + + ty += CHECK_HEIGHT + margin_height; } fullScreenModeGroup->end(); @@ -933,6 +966,17 @@ void OptionsDialog::handleClipboard(Fl_Widget *widget, void *data) #endif } +void OptionsDialog::handleFullScreenMode(Fl_Widget *widget, void *data) +{ + OptionsDialog *dialog = (OptionsDialog*)data; + + if (dialog->selectedMonitorsButton->value()) { + dialog->monitorArrangement->activate(); + } else { + dialog->monitorArrangement->deactivate(); + } +} + void OptionsDialog::handleCancel(Fl_Widget *widget, void *data) { OptionsDialog *dialog = (OptionsDialog*)data; diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h index 1c78ca16..dcba8136 100644 --- a/vncviewer/OptionsDialog.h +++ b/vncviewer/OptionsDialog.h @@ -30,6 +30,7 @@ class Fl_Round_Button; class Fl_Input; class Fl_Int_Input; class Fl_Choice; +class MonitorArrangement; typedef void (OptionsCallback)(void*); @@ -66,6 +67,8 @@ protected: static void handleClipboard(Fl_Widget *widget, void *data); + static void handleFullScreenMode(Fl_Widget *widget, void *data); + static void handleCancel(Fl_Widget *widget, void *data); static void handleOK(Fl_Widget *widget, void *data); @@ -128,6 +131,8 @@ protected: Fl_Round_Button *currentMonitorButton; Fl_Round_Button *allMonitorsButton; + Fl_Round_Button *selectedMonitorsButton; + MonitorArrangement *monitorArrangement; /* Misc. */ Fl_Check_Button *sharedCheckbox; diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx index 1525f13f..46ad132f 100644 --- a/vncviewer/parameters.cxx +++ b/vncviewer/parameters.cxx @@ -103,11 +103,15 @@ IntParameter qualityLevel("QualityLevel", BoolParameter maximize("Maximize", "Maximize viewer window", false); BoolParameter fullScreen("FullScreen", "Enable full screen", false); StringParameter fullScreenMode("FullScreenMode", "Specify which monitors to use when in full screen. " - "Should be either Current or All", + "Should be either Current, Selected or All", "Current"); BoolParameter fullScreenAllMonitors("FullScreenAllMonitors", "[DEPRECATED] Enable full screen over all monitors", false); +MonitorIndicesParameter fullScreenSelectedMonitors("FullScreenSelectedMonitors", + "Use the given list of monitors in full screen" + " when -FullScreenMode=Selected.", + "1"); StringParameter desktopSize("DesktopSize", "Reconfigure desktop size on the server on " "connect (if possible)", ""); @@ -179,6 +183,7 @@ static VoidParameter* parameterArray[] = { &qualityLevel, &fullScreen, &fullScreenMode, + &fullScreenSelectedMonitors, &desktopSize, &remoteResize, &viewOnly, diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h index fee37ee8..614e577f 100644 --- a/vncviewer/parameters.h +++ b/vncviewer/parameters.h @@ -21,6 +21,7 @@ #define __PARAMETERS_H__ #include +#include "MonitorIndicesParameter.h" #ifdef _WIN32 #include @@ -51,6 +52,7 @@ extern rfb::BoolParameter maximize; extern rfb::BoolParameter fullScreen; extern rfb::StringParameter fullScreenMode; extern rfb::BoolParameter fullScreenAllMonitors; // deprecated +extern MonitorIndicesParameter fullScreenSelectedMonitors; extern rfb::StringParameter desktopSize; extern rfb::StringParameter geometry; extern rfb::BoolParameter remoteResize; diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man index ff81b914..062bba9b 100644 --- a/vncviewer/vncviewer.man +++ b/vncviewer/vncviewer.man @@ -211,9 +211,17 @@ full-screen mode. Replaced by \fB-FullScreenMode=all\fP . .TP .B \-FullScreenMode \fImode\fP -Specify which monitors to use when in full screen. It should be either "Current" or "All". +Specify which monitors to use when in full screen. It should be either "Current", +"Selected" (specified by \fB-FullScreenSelectedMonitors\fP) or "All". The default is "Current". . +.TP +.B \-FullScreenSelectedMonitors \fImonitors\fP +This option specifies the monitors to use with \fB-FullScreenMode=selected\fP. +Monitors are ordered according to the system configuration from left to right, +and in case of a conflict, from top to bottom. So, for example, "1,2,3" means +that the first, second and third monitor counting from the left should be used. +The default is "1". . .TP .B \-FullscreenSystemKeys -- 2.39.5