202 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			202 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#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;
							 | 
						||
| 
								 | 
							
								}
							 |