1 /*
   2  * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <stdlib.h>
  27 
  28 #include "glass_screen.h"
  29 #include "glass_general.h"
  30 #include "glass_wrapper.h"
  31 
  32 #include <X11/Xatom.h>
  33 #include <gdk/gdk.h>
  34 #include <gdk/gdkx.h>
  35 
  36 jfloat OverrideUIScale = -1.0f;
  37 
  38 static guint get_current_desktop(GdkScreen *screen) {
  39     Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());
  40     Atom currentDesktopAtom = XInternAtom(display, "_NET_CURRENT_DESKTOP", True);
  41     guint ret = 0;
  42 
  43     Atom type;
  44     int format;
  45     gulong num, left;
  46     unsigned long *data = NULL;
  47 
  48     if (currentDesktopAtom == None) {
  49         return 0;
  50     }
  51 
  52     int result = XGetWindowProperty(display,
  53                                     GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
  54                                     currentDesktopAtom, 0, G_MAXLONG, False, XA_CARDINAL,
  55                                     &type, &format, &num, &left, (unsigned char **)&data);
  56 
  57     if ((result == Success) && (data != NULL)) {
  58         if (type == XA_CARDINAL && format == 32) {
  59             ret = data[0];
  60         }
  61 
  62         XFree(data);
  63     }
  64 
  65     return ret;
  66 
  67 }
  68 
  69 static GdkRectangle get_screen_workarea(GdkScreen *screen) {
  70     Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());
  71     GdkRectangle ret = { 0, 0, gdk_screen_get_width(screen), gdk_screen_get_height(screen)};
  72 
  73     Atom workareaAtom = XInternAtom(display, "_NET_WORKAREA", True);
  74 
  75     Atom type;
  76     int format;
  77     gulong num, left;
  78     unsigned long *data = NULL;
  79 
  80     if (workareaAtom == None) {
  81         return ret;
  82     }
  83 
  84     int result = XGetWindowProperty(display,
  85                                     GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
  86                                     workareaAtom, 0, G_MAXLONG, False, AnyPropertyType,
  87                                     &type, &format, &num, &left, (unsigned char **)&data);
  88 
  89     if ((result == Success) && (data != NULL)) {
  90         if (type != None && format == 32) {
  91             guint current_desktop = get_current_desktop(screen);
  92             if (current_desktop < num / 4) {
  93                 ret.x = data[current_desktop * 4];
  94                 ret.y = data[current_desktop * 4 + 1];
  95                 ret.width = data[current_desktop * 4 + 2];
  96                 ret.height = data[current_desktop * 4 + 3];
  97             }
  98         }
  99 
 100         XFree(data);
 101     }
 102 
 103     return ret;
 104 
 105 }
 106 
 107 jfloat getUIScale() {
 108     jfloat uiScale;
 109     if (OverrideUIScale > 0.0f) {
 110         uiScale = OverrideUIScale;
 111     } else {
 112         char *scale_str = getenv("GDK_SCALE");
 113         int gdk_scale = (scale_str == NULL) ? -1 : atoi(scale_str);
 114         if (gdk_scale > 0) {
 115             uiScale = (jfloat) gdk_scale;
 116         } else {
 117             uiScale = (jfloat) glass_settings_get_guint_opt("org.gnome.desktop.interface",
 118                                                             "scaling-factor", 0);
 119             if (uiScale < 1) {
 120                 uiScale = 1;
 121             }
 122         }
 123     }
 124     return uiScale;
 125 }
 126 
 127 static jobject createJavaScreen(JNIEnv* env, GdkScreen* screen, gint monitor_idx)
 128 {
 129     GdkRectangle workArea = get_screen_workarea(screen);
 130     LOG4("Work Area: x:%d, y:%d, w:%d, h:%d\n", workArea.x, workArea.y, workArea.width, workArea.height);
 131 
 132     GdkRectangle monitor_geometry;
 133     gdk_screen_get_monitor_geometry(screen, monitor_idx, &monitor_geometry);
 134     LOG1("convert monitor[%d] -> glass Screen\n", monitor_idx)
 135     LOG4("[x: %d y: %d w: %d h: %d]\n",
 136          monitor_geometry.x, monitor_geometry.y,
 137          monitor_geometry.width, monitor_geometry.height)
 138 
 139     GdkVisual* visual = gdk_screen_get_system_visual(screen);
 140 
 141     GdkRectangle working_monitor_geometry;
 142     gdk_rectangle_intersect(&workArea, &monitor_geometry, &working_monitor_geometry);
 143 
 144     jfloat uiScale = getUIScale();
 145 
 146     jint mx = monitor_geometry.x / uiScale;
 147     jint my = monitor_geometry.y / uiScale;
 148     jint mw = monitor_geometry.width / uiScale;
 149     jint mh = monitor_geometry.height / uiScale;
 150     jint wx = working_monitor_geometry.x / uiScale;
 151     jint wy = working_monitor_geometry.y / uiScale;
 152     jint ww = working_monitor_geometry.width / uiScale;
 153     jint wh = working_monitor_geometry.height / uiScale;
 154 
 155     gint mmW = gdk_screen_get_monitor_width_mm(screen, monitor_idx);
 156     gint mmH = gdk_screen_get_monitor_height_mm(screen, monitor_idx);
 157     if (mmW <= 0 || mmH <= 0) {
 158         if (gdk_screen_get_n_monitors(screen) == 1) {
 159             mmW = gdk_screen_get_width_mm(screen);
 160             mmH = gdk_screen_get_height_mm(screen);
 161         }
 162     }
 163     jint dpiX, dpiY;
 164     if (mmW <= 0 || mmH <= 0) {
 165         dpiX = dpiY = 96;
 166     } else {
 167         dpiX = (mw * 254) / (mmW * 10);
 168         dpiY = (mh * 254) / (mmH * 10);
 169     }
 170 
 171     jobject jScreen = env->NewObject(jScreenCls, jScreenInit,
 172                                      (jlong)monitor_idx,
 173 
 174                                      (visual ? glass_gdk_visual_get_depth(visual) : 0),
 175 
 176                                      mx, my, mw, mh,
 177 
 178                                      monitor_geometry.x,
 179                                      monitor_geometry.y,
 180                                      monitor_geometry.width,
 181                                      monitor_geometry.height,
 182 
 183                                      wx, wy, ww, wh,
 184 
 185                                      dpiX, dpiY,
 186                                      uiScale, uiScale, uiScale, uiScale);
 187 
 188     JNI_EXCEPTION_TO_CPP(env);
 189     return jScreen;
 190 }
 191 
 192 jobject createJavaScreen(JNIEnv* env, gint monitor_idx) {
 193     GdkScreen *default_gdk_screen = gdk_screen_get_default();
 194     try {
 195         return createJavaScreen(env, default_gdk_screen, monitor_idx);
 196     } catch (jni_exception&) {
 197         return NULL;
 198     }
 199 }
 200 
 201 jobjectArray rebuild_screens(JNIEnv* env) {
 202     GdkScreen *default_gdk_screen = gdk_screen_get_default();
 203     gint n_monitors = gdk_screen_get_n_monitors(default_gdk_screen);
 204 
 205     jobjectArray jscreens = env->NewObjectArray(n_monitors, jScreenCls, NULL);
 206     JNI_EXCEPTION_TO_CPP(env)
 207     LOG1("Available monitors: %d\n", n_monitors)
 208 
 209     int i;
 210     for (i=0; i < n_monitors; i++) {
 211         env->SetObjectArrayElement(jscreens, i, createJavaScreen(env, default_gdk_screen, i));
 212         JNI_EXCEPTION_TO_CPP(env)
 213     }
 214 
 215     return jscreens;
 216 }
 217 
 218 
 219 glong getScreenPtrForLocation(gint x, gint y) {
 220     //Note: we are relying on the fact that javafx_screen_id == gdk_monitor_id
 221     return gdk_screen_get_monitor_at_point(gdk_screen_get_default(), x, y);
 222 }
 223 
 224 void screen_settings_changed(GdkScreen* screen, gpointer user_data) {
 225     (void)screen;
 226     (void)user_data;
 227 
 228     mainEnv->CallStaticVoidMethod(jScreenCls, jScreenNotifySettingsChanged);
 229     LOG_EXCEPTION(mainEnv);
 230 }