202 lines
5.8 KiB
C
Raw Normal View History

2025-01-22 16:18:30 +01:00
#include <kinc/backend/Windows.h>
#include <kinc/display.h>
#include <kinc/error.h>
#include <kinc/log.h>
#undef RegisterClass
#define MAXIMUM_DISPLAYS 16
typedef struct {
struct HMONITOR__ *monitor;
char name[32];
bool primary, available, mode_changed;
int index, x, y, width, height, ppi, frequency, bpp;
} DisplayData;
static DisplayData displays[MAXIMUM_DISPLAYS];
static DEVMODEA original_modes[MAXIMUM_DISPLAYS];
static int screen_counter = 0;
static bool display_initialized = false;
typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
typedef HRESULT(WINAPI *GetDpiForMonitorType)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY);
static GetDpiForMonitorType MyGetDpiForMonitor = NULL;
static BOOL CALLBACK EnumerationCallback(HMONITOR monitor, HDC hdc_unused, LPRECT rect_unused, LPARAM lparam) {
MONITORINFOEXA info;
memset(&info, 0, sizeof(MONITORINFOEXA));
info.cbSize = sizeof(MONITORINFOEXA);
if (GetMonitorInfoA(monitor, (MONITORINFO *)&info) == FALSE) {
return FALSE;
}
int free_slot = 0;
for (; free_slot < MAXIMUM_DISPLAYS; ++free_slot) {
if (displays[free_slot].monitor == monitor) {
return FALSE;
}
if (displays[free_slot].monitor == NULL) {
break;
}
}
DisplayData *display = &displays[free_slot];
strncpy(display->name, info.szDevice, 31);
display->name[31] = 0;
display->index = free_slot;
display->monitor = monitor;
display->primary = (info.dwFlags & MONITORINFOF_PRIMARY) != 0;
display->available = true;
display->x = info.rcMonitor.left;
display->y = info.rcMonitor.top;
display->width = info.rcMonitor.right - info.rcMonitor.left;
display->height = info.rcMonitor.bottom - info.rcMonitor.top;
HDC hdc = CreateDCA(NULL, display->name, NULL, NULL);
display->ppi = GetDeviceCaps(hdc, LOGPIXELSX);
int scale = GetDeviceCaps(hdc, SCALINGFACTORX);
DeleteDC(hdc);
if (MyGetDpiForMonitor != NULL) {
unsigned dpiX, dpiY;
MyGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
display->ppi = (int)dpiX;
}
memset(&original_modes[free_slot], 0, sizeof(DEVMODEA));
original_modes[free_slot].dmSize = sizeof(DEVMODEA);
EnumDisplaySettingsA(display->name, ENUM_CURRENT_SETTINGS, &original_modes[free_slot]);
display->frequency = original_modes[free_slot].dmDisplayFrequency;
display->bpp = original_modes[free_slot].dmBitsPerPel;
++screen_counter;
return TRUE;
}
void kinc_display_init() {
if (display_initialized) {
return;
}
HMODULE shcore = LoadLibraryA("Shcore.dll");
if (shcore != NULL) {
MyGetDpiForMonitor = (GetDpiForMonitorType)GetProcAddress(shcore, "GetDpiForMonitor");
}
memset(displays, 0, sizeof(DisplayData) * MAXIMUM_DISPLAYS);
EnumDisplayMonitors(NULL, NULL, EnumerationCallback, 0);
display_initialized = true;
}
int kinc_windows_get_display_for_monitor(struct HMONITOR__ *monitor) {
for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) {
if (displays[i].monitor == monitor) {
return i;
}
}
kinc_error_message("Display for monitor not found");
return -1;
}
int kinc_count_displays() {
return screen_counter;
}
int kinc_primary_display() {
for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) {
DisplayData *display = &displays[i];
if (display->available && display->primary) {
return i;
}
}
kinc_error_message("No primary display defined");
return -1;
}
kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index) {
DEVMODEA dev_mode = {0};
dev_mode.dmSize = sizeof(DEVMODEA);
EnumDisplaySettingsA(displays[display_index].name, mode_index, &dev_mode);
kinc_display_mode_t mode;
mode.x = displays[display_index].x;
mode.y = displays[display_index].y;
mode.width = dev_mode.dmPelsWidth;
mode.height = dev_mode.dmPelsHeight;
mode.frequency = dev_mode.dmDisplayFrequency;
mode.bits_per_pixel = dev_mode.dmBitsPerPel;
mode.pixels_per_inch = displays[display_index].ppi * mode.width / original_modes[display_index].dmPelsWidth;
return mode;
}
int kinc_display_count_available_modes(int display_index) {
DEVMODEA dev_mode = {0};
dev_mode.dmSize = sizeof(DEVMODEA);
int i = 0;
for (; EnumDisplaySettingsA(displays[display_index].name, i, &dev_mode) != FALSE; ++i)
;
return i;
}
bool kinc_windows_set_display_mode(int display_index, int width, int height, int bpp, int frequency) {
DisplayData *display = &displays[display_index];
display->mode_changed = true;
DEVMODEA mode = {0};
mode.dmSize = sizeof(mode);
strcpy((char *)mode.dmDeviceName, display->name);
mode.dmPelsWidth = width;
mode.dmPelsHeight = height;
mode.dmBitsPerPel = bpp;
mode.dmDisplayFrequency = frequency;
mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
bool success = ChangeDisplaySettingsA(&mode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
if (!success) {
mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
success = ChangeDisplaySettingsA(&mode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
}
return success;
}
void kinc_windows_restore_display(int display) {
if (displays[display].mode_changed) {
ChangeDisplaySettingsA(&original_modes[display], 0);
}
}
void kinc_windows_restore_displays() {
for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) {
kinc_windows_restore_display(i);
}
}
bool kinc_display_available(int display_index) {
if (display_index < 0 || display_index >= MAXIMUM_DISPLAYS) {
return false;
}
return displays[display_index].available;
}
const char *kinc_display_name(int display_index) {
return displays[display_index].name;
}
kinc_display_mode_t kinc_display_current_mode(int display_index) {
DisplayData *display = &displays[display_index];
kinc_display_mode_t mode;
mode.x = display->x;
mode.y = display->y;
mode.width = display->width;
mode.height = display->height;
mode.pixels_per_inch = display->ppi;
mode.frequency = display->frequency;
mode.bits_per_pixel = display->bpp;
return mode;
}