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; | ||
|  | } |