aboutsummaryrefslogtreecommitdiffstats
path: root/vncviewer/MonitorArrangement.cxx
diff options
context:
space:
mode:
authorHugo Lundin <hugo@lundin.dev>2021-07-13 15:55:54 +0200
committerHugo Lundin <hugo@lundin.dev>2021-07-16 16:09:46 +0200
commit94f52f9edf11bcd30df76b2da35f614c23a81c2f (patch)
treec09a070ccec1bd0eac31c91e5ff40b6f8d32f642 /vncviewer/MonitorArrangement.cxx
parenta182475db57b3da5edf3d10542086397ee022dcd (diff)
downloadtigervnc-94f52f9edf11bcd30df76b2da35f614c23a81c2f.tar.gz
tigervnc-94f52f9edf11bcd30df76b2da35f614c23a81c2f.zip
Add monitor description to tooltip
It might be useful to have more information about a monitor when configuring its settings in the Options menu. Therefore, this commit adds support for showing additional information about a monitor (resolution and platform-specific name).
Diffstat (limited to 'vncviewer/MonitorArrangement.cxx')
-rw-r--r--vncviewer/MonitorArrangement.cxx164
1 files changed, 164 insertions, 0 deletions
diff --git a/vncviewer/MonitorArrangement.cxx b/vncviewer/MonitorArrangement.cxx
index e5ff3652..83a53d38 100644
--- a/vncviewer/MonitorArrangement.cxx
+++ b/vncviewer/MonitorArrangement.cxx
@@ -28,10 +28,31 @@
#include <FL/fl_draw.H>
#include <FL/Fl_Button.H>
+#if defined(HAVE_XRANDR) && !defined(__APPLE__)
+#include <X11/extensions/Xrandr.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#endif
+
+#ifdef __APPLE__
+#include <Carbon/Carbon.h>
+#include <IOKit/graphics/IOGraphicsLib.h>
+#include <IOKit/hidsystem/IOHIDLib.h>
+#include <IOKit/hidsystem/IOHIDParameter.h>
+#endif
+
#include <rfb/Rect.h>
+#include <rfb/LogWriter.h>
+
+#ifdef WIN32
+#include "win32.h"
+#endif
+#include "i18n.h"
#include "MonitorArrangement.h"
+static rfb::LogWriter vlog("MonitorArrangement");
static const Fl_Boxtype FL_CHECKERED_BOX = FL_FREE_BOXTYPE;
MonitorArrangement::MonitorArrangement(
@@ -116,6 +137,9 @@ void MonitorArrangement::layout()
monitor->when(FL_WHEN_CHANGED);
m_monitors.push_back(monitor);
}
+
+ for (int i = 0; i < (int) m_monitors.size(); i++)
+ m_monitors[i]->copy_tooltip(description(i).c_str());
}
bool MonitorArrangement::is_required(int m)
@@ -255,6 +279,146 @@ std::pair<int, int> MonitorArrangement::origin()
return std::make_pair(ox, oy);
}
+std::string MonitorArrangement::description(int m)
+{
+ assert(m < (int) m_monitors.size());
+ const size_t name_len = 1024;
+ char name[name_len] = {};
+ int bytes_written = get_monitor_name(m, name, name_len);
+
+ int x, y, w, h;
+ Fl::screen_xywh(x, y, w, h, m);
+ std::stringstream ss;
+
+ if (bytes_written > 0)
+ ss << name << " (" << w << "x" << h << ")";
+ else
+ ss << w << "x" << h;
+
+ return ss.str();
+}
+
+int MonitorArrangement::get_monitor_name(int m, char name[], size_t name_len)
+{
+#if defined(WIN32)
+ int x, y, w, h;
+ Fl::screen_xywh(x, y, w, h, m);
+ return win32_get_monitor_name(x, y, w, h, name, name_len);
+
+#elif defined(__APPLE__)
+ CGDisplayCount count;
+ int bytes_written = 0;
+ CGDirectDisplayID displays[16];
+
+ if (CGGetActiveDisplayList(16, displays, &count) != kCGErrorSuccess)
+ return -1;
+
+ if (count != (unsigned)Fl::screen_count())
+ return -1;
+
+ if (m >= (int)count)
+ return -1;
+
+ // Notice: Here we assume indices to be ordered the same as in FLTK (we rely on that in cocoa.mm as well).
+ CGDirectDisplayID displayID = displays[m];
+
+ CFDictionaryRef info = IODisplayCreateInfoDictionary(
+ /* display = */ CGDisplayIOServicePort(displayID),
+ /* options = */ kIODisplayOnlyPreferredName);
+
+ CFDictionaryRef dict = (CFDictionaryRef) CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
+ CFIndex dict_len = CFDictionaryGetCount(dict);
+
+ if (dict_len > 0) {
+ CFTypeRef * names = new CFTypeRef[dict_len];
+ CFDictionaryGetKeysAndValues(dict, NULL, (const void **) names);
+
+ if (names[0]) {
+
+ // Because of `kIODisplayOnlyPreferredName` names *should* only contain the name with
+ // the current system localization.
+ CFStringRef localized_name = (CFStringRef) names[0];
+ CFIndex localized_name_len = CFStringGetLength(localized_name);
+
+ // Even though we already have the length of `localized_name` above, we know that we will format it
+ // as UTF-8 when we put it in the destination buffer. Therefore we need to check whether the name
+ // with that encoding will fit.
+ CFIndex localized_name_max_size = CFStringGetMaximumSizeForEncoding(localized_name_len, kCFStringEncodingUTF8) + 1;
+
+ if (name_len > (size_t)localized_name_max_size) {
+ if (CFStringGetCString(
+ /* ref = */ localized_name,
+ /* dest = */ name,
+ /* dest_len = */ name_len,
+ /* encoding = */ kCFStringEncodingUTF8))
+ {
+ bytes_written = strlen(name);
+ }
+ }
+ }
+
+ delete[] names;
+ }
+
+ CFRelease(info);
+ return bytes_written;
+
+#else
+#if defined (HAVE_XRANDR)
+ int x, y, w, h;
+ int ev, err, xi_major;
+
+ fl_open_display();
+ assert(fl_display != NULL);
+ Fl::screen_xywh(x, y, w, h, m);
+
+ if (!XQueryExtension(fl_display, "RANDR", &xi_major, &ev, &err)) {
+ vlog.info(_("Failed to get monitor name because X11 RandR could not be found."));
+ return -1;
+ }
+
+ XRRScreenResources *res = XRRGetScreenResources(fl_display, DefaultRootWindow(fl_display));
+ if (!res) {
+ vlog.error(_("Failed to get XRRScreenResources for root window."));
+ return -1;
+ }
+
+ for (int i = 0; i < res->ncrtc; i++) {
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(fl_display, res, res->crtcs[i]);
+
+ if (!crtc) {
+ vlog.error(_("Failed to get XRRCrtcInfo for crtc %d"), i);
+ continue;
+ }
+
+ for (int j = 0; j < crtc->noutput; j++) {
+ bool monitor_found = (crtc->x == x) &&
+ (crtc->y == y) &&
+ (crtc->width == ((unsigned int) w)) &&
+ (crtc->height == ((unsigned int) h));
+
+ if (monitor_found) {
+ XRROutputInfo *output = XRRGetOutputInfo(fl_display, res, crtc->outputs[j]);
+ if (!output) {
+ vlog.error(_("Failed to get XRROutputInfo for crtc %d, output %d."), i, j);
+ continue;
+ }
+
+ if (strlen(output->name) >= name_len)
+ return -1;
+
+ return snprintf(name, name_len, "%.*s", (int)name_len, output->name);
+ }
+ }
+ }
+
+ return -1;
+
+#endif // !HAVE_XRANDR
+ return 0;
+#endif
+}
+
void MonitorArrangement::monitor_pressed(Fl_Widget *widget, void *user_data)
{
MonitorArrangement *self = (MonitorArrangement *) user_data;