[时间:2017-05] [状态:Open]
[关键词:sdl2,屏幕分辨率,显示区域,多媒体渲染,窗口,sdl2源码分析]0 引言
本文的主要目标在于使用SDL2获得屏幕相关的属性,比如分辨率、屏幕个数以及屏幕可用区域的范围。
通常情况下,有过图形界面编程经验的人都知道桌面系统的构成,屏幕分辨率是指的整个屏幕区域的宽高,而通常屏幕区域有一些系统的任务栏或者菜单栏;举个例子,windows下的任务栏一般位于下面,并且通常非全屏窗口是不能占用任务栏的。
1 获得屏幕个数
SDL2中提供了获得屏幕个数的接口SDL_GetNumVideoDisplays
,具体建议参考。
当然屏幕还有其他属性,比如显示模式等,可以通过SDL_GetNumDisplayModes
和SDL_GetDisplayMode
获得SDL_DisplayMode
的结构,其中包括显示格式(YUV、RGB等)、宽高、刷新率等。
2 每个屏幕的分辨率
SDL_GetDisplayBounds
可以获得指定屏幕的显示区域,可以通过显示区域获取屏幕分辨率。
3 获取实际显示区域的大小
SDL_GetDisplayUsableBounds
返回的是实际可用显示区域的大小,这个通常比屏幕分辨率小。
4 SDL2内部实现的原理分析
这里以SDL_GetDisplayBounds
为例说明,如果读者对其他函数感兴趣,可以查看sdl2代码。
int SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect){ CHECK_DISPLAY_INDEX(displayIndex, -1); if (rect) { SDL_VideoDisplay *display = &_this->displays[displayIndex]; if (_this->GetDisplayBounds) { if (_this->GetDisplayBounds(_this, display, rect) == 0) { return 0; } } /* Assume that the displays are left to right */ if (displayIndex == 0) { rect->x = 0; rect->y = 0; } else { SDL_GetDisplayBounds(displayIndex-1, rect); rect->x += rect->w; } rect->w = display->current_mode.w; rect->h = display->current_mode.h; } return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */}
很明显这个函数直接调用了SDL_VideoDevice->GetDisplayBounds
函数,那么我们找一个实现版本看看,比如windows下的,代码如下:
static SDL_VideoDevice * WIN_CreateDevice(int devindex){ // ... device->GetDisplayBounds = WIN_GetDisplayBounds; // ...}
这个实现被重定向到WIN_GetDisplayBounds函数中,代码如下:
intWIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect){ SDL_DisplayModeData *data = (SDL_DisplayModeData *) display->current_mode.driverdata; rect->x = (int)SDL_ceil(data->DeviceMode.dmPosition.x * data->ScaleX); rect->y = (int)SDL_ceil(data->DeviceMode.dmPosition.y * data->ScaleY); rect->w = (int)SDL_ceil(data->DeviceMode.dmPelsWidth * data->ScaleX); rect->h = (int)SDL_ceil(data->DeviceMode.dmPelsHeight * data->ScaleY); return 0;}
实现比较简单,直接从display->current_mode中获得数据。那么这个mode在哪里赋值的呢?
在SDL_windowsvideo.c中查找下,发下如下调用:static SDL_bool WIN_AddDisplay(_THIS, LPTSTR DeviceName){ SDL_VideoDisplay display; SDL_DisplayData *displaydata; SDL_DisplayMode mode; DISPLAY_DEVICE device; if (!WIN_GetDisplayMode(_this, DeviceName, ENUM_CURRENT_SETTINGS, &mode)) { return SDL_FALSE; } displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata)); if (!displaydata) { return SDL_FALSE; } SDL_memcpy(displaydata->DeviceName, DeviceName, sizeof(displaydata->DeviceName)); SDL_zero(display); device.cb = sizeof(device); if (EnumDisplayDevices(DeviceName, 0, &device, 0)) { display.name = WIN_StringToUTF8(device.DeviceString); } display.desktop_mode = mode; display.current_mode = mode; display.driverdata = displaydata; SDL_AddVideoDisplay(&display); SDL_free(display.name); return SDL_TRUE;}
明显这是从中获取。
至此所有实现逻辑基本理清。5 小结
本文主要整理我近期遇到的SDL2关于窗口和屏幕属性获取的逻辑,同时分析了SDL_GetDisplayBounds
在windows上的实现逻辑。