Unverified Commit 108f7eb3 authored by Roman Sedaikin's avatar Roman Sedaikin Committed by GitHub

Getting display's physical dpi (#505)

* Added getting physical monitor dpi on Windows.
* Added getting physical monitor dpi on Linux.
* Added getting physical monitor dpi on Macos.
* Moved xrandr functions to separate files.
* Added docs.
parent c26514f7
......@@ -72,8 +72,18 @@ fun createWindow(title: String, exitOnClose: Boolean) = SwingUtilities.invokeLat
}
})
val miDpiState = JMenuItem("Get current DPI")
val ctrlD = KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())
miDpiState.setAccelerator(ctrlD)
miDpiState.addActionListener(object : ActionListener {
override fun actionPerformed(actionEvent: ActionEvent?) {
println("DPI: ${skiaLayer.currentDPI}")
}
})
fileMenu.add(miToggleFullscreen)
fileMenu.add(miFullscreenState)
fileMenu.add(miDpiState)
fileMenu.add(miTakeScreenshot)
val editMenu = JMenu("Edit")
......
......@@ -67,6 +67,9 @@ internal open class HardwareLayer(
val windowHandle: Long
get() = useDrawingSurfacePlatformInfo(::getWindowHandle)
val currentDPI: Int
get() = useDrawingSurfacePlatformInfo(::getCurrentDPI)
val contentScale: Float
get() = _contentScale!!
......@@ -80,6 +83,7 @@ internal open class HardwareLayer(
private external fun getContentHandle(platformInfo: Long): Long
private external fun getWindowHandle(platformInfo: Long): Long
private external fun getCurrentDPI(platformInfo: Long): Int
private val _externalAccessible = externalAccessibleFactory?.invoke(this)
private var _focusedAccessible: Accessible? = null
......
......@@ -135,12 +135,26 @@ actual open class SkiaLayer internal constructor(
actual val contentScale: Float
get() = backedLayer.contentScale
/**
* Returns the pointer to an OS specific handle (native resource) of the [SkiaLayer].
*/
val contentHandle: Long
get() = backedLayer.contentHandle
/**
* Returns the pointer to an OS specific window handle (native resource)
* which the current [SkiaLayer] is attached.
*/
val windowHandle: Long
get() = backedLayer.windowHandle
/**
* Returns the physical DPI value (number of dots per inch)
* of the current monitor.
*/
val currentDPI: Int
get() = backedLayer.currentDPI
actual var fullscreen: Boolean
get() = fullscreenAdapter.fullscreen
set(value) {
......
#if SK_BUILD_FOR_LINUX
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/extensions/Xrandr.h>
void* loadXrandr();
XRRScreenResources* XRRGetScreenResourcesCurrentDynamic(Display* display, Window window);
XRRCrtcInfo* XRRGetCrtcInfoDynamic(Display *display, XRRScreenResources *resources, RRCrtc crtc);
XRROutputInfo* XRRGetOutputInfoDynamic(Display *display, XRRScreenResources *resources, RROutput output);
void XRRFreeCrtcInfoDynamic(XRRCrtcInfo* crtcInfo);
void XRRFreeOutputInfoDynamic(XRROutputInfo* outputInfo);
void XRRFreeScreenResourcesDynamic(XRRScreenResources *resources);
#endif
\ No newline at end of file
#include <jawt_md.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/extensions/Xrandr.h>
#include <cstdlib>
#include <dlfcn.h>
#include <unistd.h>
#include <stdio.h>
#include "xrandr_utils.h"
#include "jni_helpers.h"
static void* loadXrandr() {
static void* result = nullptr;
if (result != nullptr) return result;
result = dlopen("libXrandr.so", RTLD_LAZY | RTLD_LOCAL);
return result;
}
static XRRScreenResources* XRRGetScreenResourcesCurrentDynamic(Display* display, Window window) {
typedef XRRScreenResources* (*XRRGetScreenResourcesCurrent_t)(Display*, Window);
static XRRGetScreenResourcesCurrent_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return nullptr;
func = (XRRGetScreenResourcesCurrent_t)dlsym(lib, "XRRGetScreenResourcesCurrent");
}
if (!func) return nullptr;
return func(display, window);
}
static XRRCrtcInfo* XRRGetCrtcInfoDynamic(
Display *display, XRRScreenResources *resources, RRCrtc crtc) {
typedef XRRCrtcInfo* (*XRRGetCrtcInfo_t)(Display*, XRRScreenResources*, RRCrtc);
static XRRGetCrtcInfo_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return nullptr;
func = (XRRGetCrtcInfo_t)dlsym(lib, "XRRGetCrtcInfo");
}
if (!func) return nullptr;
return func(display, resources, crtc);
}
void XRRFreeCrtcInfoDynamic(XRRCrtcInfo* crtcInfo) {
typedef void (*XRRFreeCrtcInfo_t)(XRRCrtcInfo*);
static XRRFreeCrtcInfo_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return;
func = (XRRFreeCrtcInfo_t)dlsym(lib, "XRRFreeCrtcInfo");
}
if (!func) return;
func(crtcInfo);
}
void XRRFreeScreenResourcesDynamic(XRRScreenResources *resources) {
typedef void (*XRRFreeScreenResources_t)(XRRScreenResources*);
static XRRFreeScreenResources_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return;
func = (XRRFreeScreenResources_t)dlsym(lib, "XRRFreeScreenResources");
}
if (!func) return;
func(resources);
}
extern "C"
{
JNIEXPORT jdouble JNICALL Java_org_jetbrains_skiko_DisplayKt_getLinuxDisplayRefreshRate(JNIEnv *env, jobject obj, jlong displayPtr, jlong windowPtr)
......
#include <jawt_md.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <cstdlib>
#include <dlfcn.h>
#include <unistd.h>
#include <stdio.h>
#include <vector>
#include "xrandr_utils.h"
#include "jni_helpers.h"
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
......@@ -93,4 +93,108 @@ extern "C"
return (float) getDpiScale();
}
struct MonitorInfo {
int x;
int y;
int wPx;
int hPx;
int wMm;
int hMm;
};
int getIntersectSquare(int xR, int yR, int wR, int hR, int xA, int yA, int wA, int hA) {
xR = xR - xA;
xA = 0;
yR = yR - yA;
yA = 0;
if (xR < 0) {
wR = xR + wR;
xR = 0;
}
if (yR < 0) {
hR = yR + hR;
yR = 0;
}
if (xR + wR > xA + wA) {
wR = wA - xR;
}
if (yR + hR > yA + hA) {
hR = hA - yR;
}
return wR * hA;
}
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_HardwareLayer_getCurrentDPI(JNIEnv *env, jobject canvas, jlong platformInfoPtr)
{
JAWT_X11DrawingSurfaceInfo *dsi_x11 = fromJavaPointer<JAWT_X11DrawingSurfaceInfo *>(platformInfoPtr);
Display *display = dsi_x11->display;
Window window = (Window)Java_org_jetbrains_skiko_HardwareLayer_getWindowHandle(env, canvas, platformInfoPtr);
XRRScreenResources * res = XRRGetScreenResourcesCurrentDynamic(display, window);
XRRCrtcInfo *crtc_info;
std::vector<MonitorInfo> monitors;
for (int i = 0; i < res->ncrtc; i++)
{
XRROutputInfo * output_info = XRRGetOutputInfoDynamic(display, res, res->outputs[i]);
if (output_info->connection || output_info->crtc == NULL) {
XRRFreeOutputInfoDynamic(output_info);
continue;
}
XRRCrtcInfo * crtc_info = XRRGetCrtcInfoDynamic(display, res, output_info->crtc);
MonitorInfo minfo;
minfo.x = crtc_info->x;
minfo.y = crtc_info->y;
minfo.wPx = crtc_info->width;
minfo.hPx = crtc_info->height;
if (crtc_info->rotation == RR_Rotate_90 || crtc_info->rotation == RR_Rotate_270)
{
minfo.wMm = output_info->mm_height;
minfo.hMm = output_info->mm_width;
}
else
{
minfo.wMm = output_info->mm_width;
minfo.hMm = output_info->mm_height;
}
// If XRandr does not provide a physical size, assume the X11 default 96 DPI
if (minfo.wMm <= 0 || minfo.hMm <= 0)
{
minfo.wMm = (int) (minfo.wPx * 25.4f / 96.f);
minfo.hMm = (int) (minfo.hPx * 25.4f / 96.f);
}
monitors.push_back(minfo);
XRRFreeCrtcInfoDynamic(crtc_info);
XRRFreeOutputInfoDynamic(output_info);
}
XRRFreeScreenResourcesDynamic(res);
if (monitors.size() == 1) {
return (jint)(monitors[0].wPx / (monitors[0].wMm / 25.4));
}
// Determining which monitor the current window is open on
XWindowAttributes xwa;
XGetWindowAttributes(display, window, &xwa);
int index = 0;
int maxIntersectSquare = 0;
for (int m = 0; m < monitors.size(); m++) {
int intersectSquare = getIntersectSquare(
xwa.x, xwa.y, xwa.width, xwa.height,
monitors[m].x, monitors[m].y, monitors[m].wPx, monitors[m].hPx
);
if (maxIntersectSquare < intersectSquare) {
maxIntersectSquare = intersectSquare;
index = m;
}
}
return (jint)(monitors[index].wPx / (monitors[index].wMm / 25.4));
}
} // extern "C"
\ No newline at end of file
#if SK_BUILD_FOR_LINUX
#include "xrandr_utils.h"
#include <dlfcn.h>
void* loadXrandr() {
static void* result = nullptr;
if (result != nullptr) return result;
result = dlopen("libXrandr.so", RTLD_LAZY | RTLD_LOCAL);
return result;
}
XRRScreenResources* XRRGetScreenResourcesCurrentDynamic(Display* display, Window window) {
typedef XRRScreenResources* (*XRRGetScreenResourcesCurrent_t)(Display*, Window);
static XRRGetScreenResourcesCurrent_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return nullptr;
func = (XRRGetScreenResourcesCurrent_t)dlsym(lib, "XRRGetScreenResourcesCurrent");
}
if (!func) return nullptr;
return func(display, window);
}
XRRCrtcInfo* XRRGetCrtcInfoDynamic(
Display *display, XRRScreenResources *resources, RRCrtc crtc) {
typedef XRRCrtcInfo* (*XRRGetCrtcInfo_t)(Display*, XRRScreenResources*, RRCrtc);
static XRRGetCrtcInfo_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return nullptr;
func = (XRRGetCrtcInfo_t)dlsym(lib, "XRRGetCrtcInfo");
}
if (!func) return nullptr;
return func(display, resources, crtc);
}
XRROutputInfo* XRRGetOutputInfoDynamic(
Display *display, XRRScreenResources *resources, RROutput output) {
typedef XRROutputInfo* (*XRRGetOutputInfo_t)(Display*, XRRScreenResources*, RROutput);
static XRRGetOutputInfo_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return nullptr;
func = (XRRGetOutputInfo_t)dlsym(lib, "XRRGetOutputInfo");
}
if (!func) return nullptr;
return func(display, resources, output);
}
void XRRFreeCrtcInfoDynamic(XRRCrtcInfo* crtcInfo) {
typedef void (*XRRFreeCrtcInfo_t)(XRRCrtcInfo*);
static XRRFreeCrtcInfo_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return;
func = (XRRFreeCrtcInfo_t)dlsym(lib, "XRRFreeCrtcInfo");
}
if (!func) return;
func(crtcInfo);
}
void XRRFreeOutputInfoDynamic(XRROutputInfo* outputInfo) {
typedef void (*XRRFreeOutputInfo_t)(XRROutputInfo*);
static XRRFreeOutputInfo_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return;
func = (XRRFreeOutputInfo_t)dlsym(lib, "XRRFreeOutputInfo");
}
if (!func) return;
func(outputInfo);
}
void XRRFreeScreenResourcesDynamic(XRRScreenResources *resources) {
typedef void (*XRRFreeScreenResources_t)(XRRScreenResources*);
static XRRFreeScreenResources_t func = nullptr;
if (!func) {
void* lib = loadXrandr();
if (!lib) return;
func = (XRRFreeScreenResources_t)dlsym(lib, "XRRFreeScreenResources");
}
if (!func) return;
func(resources);
}
#endif
\ No newline at end of file
#define WIN32_LEAN_AND_MEAN
#include <jawt_md.h>
#include <Windows.h>
#include <shellscalingapi.h>
#include <cassert>
#include "jni_helpers.h"
......@@ -57,4 +58,19 @@ extern "C"
return 2;
}
}
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_HardwareLayer_getCurrentDPI(JNIEnv *env, jobject canvas, jlong platformInfoPtr)
{
HWND hwnd = (HWND)Java_org_jetbrains_skiko_HardwareLayer_getWindowHandle(env, canvas, platformInfoPtr);
HMONITOR display = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
UINT xDpi = 0, yDpi = 0;
GetDpiForMonitor(display, MDT_RAW_DPI, &xDpi, &yDpi);
int dpi = (int)xDpi;
// We can get dpi:0 if we set up multiple displays for content duplication (mirror).
if (dpi == 0) {
// get default system dpi
dpi = GetDpiForWindow(hwnd);
}
return dpi;
}
} // extern "C"
\ No newline at end of file
......@@ -399,6 +399,18 @@ JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_SystemTheme_1awtKt_getCurrentSys
}
}
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_HardwareLayer_getCurrentDPI(JNIEnv *env, jobject component, jlong platformInfoPtr)
{
@autoreleasepool {
LayerHandler *layer = findByObject(env, component);
NSScreen *screen = [layer.window screen];
NSDictionary *description = [screen deviceDescription];
NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
return (jint)(displayPixelSize.width / (displayPhysicalSize.width / 25.4));
}
}
void getMetalDeviceAndQueue(void** device, void** queue)
{
id<MTLDevice> fDevice = MTLCreateSystemDefaultDevice();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment