]> source.dussan.org Git - tigervnc.git/commitdiff
Add monitor description to tooltip 1282/head
authorHugo Lundin <hugo@lundin.dev>
Tue, 13 Jul 2021 13:55:54 +0000 (15:55 +0200)
committerHugo Lundin <hugo@lundin.dev>
Fri, 16 Jul 2021 14:09:46 +0000 (16:09 +0200)
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).

vncviewer/CMakeLists.txt
vncviewer/MonitorArrangement.cxx
vncviewer/MonitorArrangement.h
vncviewer/win32.c
vncviewer/win32.h

index 9d11ba79306a057748f836b15094efe598516207..9aa985af2165144769f7a54f893f73211d61dc65 100644 (file)
@@ -63,6 +63,11 @@ elseif(APPLE)
   target_link_libraries(vncviewer "-framework IOKit")
 else()
   target_link_libraries(vncviewer ${X11_Xi_LIB})
+
+  if(X11_Xrandr_LIB)
+    add_definitions(-DHAVE_XRANDR)
+    target_link_libraries(vncviewer ${X11_Xrandr_LIB})
+  endif()
 endif()
 
 install(TARGETS vncviewer DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
index e5ff36520481243dd09c249105f2585bdd26befd..83a53d3890cc58bc9f478f1db12dea43c82725a8 100644 (file)
 #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;
index c29b6ff640836f287fdad37d0adfd37418035772..ca1c4896d63c8afee7227ae59621fbdb6ebc8125 100644 (file)
@@ -64,6 +64,10 @@ private:
   // Return the origin of the monitor arrangement (top left corner).
   std::pair<int, int> origin();
 
+  // Get a textual description of the given monitor.
+  std::string description(int m);
+  int get_monitor_name(int m, char name[], size_t name_len);
+
   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);
index d116ecc3f037ed2c42ab0aadc0b739e7b08e6320..dc8cb6021acb6f6b4e2fe087f02440162d4965df 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <windows.h>
+#include <stdio.h>
 
 #define XK_MISCELLANY
 #define XK_XKB_KEYS
@@ -418,3 +419,54 @@ int win32_has_altgr(void)
 
   return has_altgr;
 }
+
+typedef struct {
+       int x, y, w, h;
+    char* name;
+       size_t name_len;
+       int bytes_written;
+} EnumCallbackData;
+
+static BOOL CALLBACK EnumDisplayMonitorsCallback(
+       HMONITOR monitor, HDC deviceContext, LPRECT rect, LPARAM userData)
+{
+       EnumCallbackData *data = (EnumCallbackData *)userData;
+       MONITORINFOEX info;
+       info.cbSize = sizeof(info);
+       GetMonitorInfo(monitor, (LPMONITORINFO)&info);
+
+       int x = info.rcMonitor.left;
+       int y = info.rcMonitor.top;
+       int w = info.rcMonitor.right - info.rcMonitor.left;
+       int h = info.rcMonitor.bottom - info.rcMonitor.top;
+
+       if ((data->x == x) && (data->y == y) && (data->w == w) && (data->h == h)) {
+               data->bytes_written = snprintf(data->name, data->name_len,
+                       "%.*s", (int)(data->name_len - 1), info.szDevice);
+
+               if (data->bytes_written < 0)
+                       return FALSE;
+
+               // Stop the iteration.
+               return FALSE;
+       }
+
+       // Keep iterating.
+       return TRUE;
+}
+
+int win32_get_monitor_name(int x, int y, int w, int h, char name[], size_t name_len)
+{
+       EnumCallbackData data = {
+               .x = x,
+               .y = y,
+               .w = w,
+               .h = h,
+               .name = name,
+               .name_len = name_len,
+               .bytes_written = -1
+       };
+
+       EnumDisplayMonitors(NULL, NULL, EnumDisplayMonitorsCallback, (LPARAM) &data);
+       return data.bytes_written;
+}
index 9d4704bcf8f310a1a91449e1eef182738ebbbaa8..2dae04b91b670fcc0c6e841a915e6df27dc0f2ec 100644 (file)
@@ -34,6 +34,7 @@ int win32_vkey_to_keysym(UINT vkey, int extended);
 
 int win32_has_altgr(void);
 
+int win32_get_monitor_name(int x, int y, int w, int h, char name[], size_t name_len);
 };
 
 #endif