video.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. #if defined(VIDEO_CGL)
  2. #include <ruby/video/cgl.cpp>
  3. #endif
  4. #if defined(VIDEO_DIRECT3D)
  5. #include <ruby/video/direct3d.cpp>
  6. #endif
  7. #if defined(VIDEO_DIRECTDRAW)
  8. #include <ruby/video/directdraw.cpp>
  9. #endif
  10. #if defined(VIDEO_GDI)
  11. #include <ruby/video/gdi.cpp>
  12. #endif
  13. #if defined(VIDEO_GLX)
  14. #include <ruby/video/glx.cpp>
  15. #endif
  16. #if defined(VIDEO_GLX2)
  17. #include <ruby/video/glx2.cpp>
  18. #endif
  19. #if defined(VIDEO_WGL)
  20. #include <ruby/video/wgl.cpp>
  21. #endif
  22. #if defined(VIDEO_XSHM)
  23. #include <ruby/video/xshm.cpp>
  24. #endif
  25. #if defined(VIDEO_XVIDEO)
  26. #include <ruby/video/xvideo.cpp>
  27. #endif
  28. namespace ruby {
  29. auto Video::setFullScreen(bool fullScreen) -> bool {
  30. if(instance->fullScreen == fullScreen) return true;
  31. if(!instance->hasFullScreen()) return false;
  32. if(!instance->setFullScreen(instance->fullScreen = fullScreen)) return false;
  33. return true;
  34. }
  35. auto Video::setMonitor(string monitor) -> bool {
  36. if(instance->monitor == monitor) return true;
  37. if(!instance->hasMonitor()) return false;
  38. if(!instance->setMonitor(instance->monitor = monitor)) return false;
  39. return true;
  40. }
  41. auto Video::setExclusive(bool exclusive) -> bool {
  42. if(instance->exclusive == exclusive) return true;
  43. if(!instance->hasExclusive()) return false;
  44. if(!instance->setExclusive(instance->exclusive = exclusive)) return false;
  45. return true;
  46. }
  47. auto Video::setContext(uintptr context) -> bool {
  48. if(instance->context == context) return true;
  49. if(!instance->hasContext()) return false;
  50. if(!instance->setContext(instance->context = context)) return false;
  51. return true;
  52. }
  53. auto Video::setBlocking(bool blocking) -> bool {
  54. if(instance->blocking == blocking) return true;
  55. if(!instance->hasBlocking()) return false;
  56. if(!instance->setBlocking(instance->blocking = blocking)) return false;
  57. return true;
  58. }
  59. auto Video::setFlush(bool flush) -> bool {
  60. if(instance->flush == flush) return true;
  61. if(!instance->hasFlush()) return false;
  62. if(!instance->setFlush(instance->flush = flush)) return false;
  63. return true;
  64. }
  65. auto Video::setFormat(string format) -> bool {
  66. if(instance->format == format) return true;
  67. if(!instance->hasFormat(format)) return false;
  68. if(!instance->setFormat(instance->format = format)) return false;
  69. return true;
  70. }
  71. auto Video::setShader(string shader) -> bool {
  72. if(instance->shader == shader) return true;
  73. if(!instance->hasShader()) return false;
  74. if(!instance->setShader(instance->shader = shader)) return false;
  75. return true;
  76. }
  77. //
  78. auto Video::focused() -> bool {
  79. return instance->focused();
  80. }
  81. auto Video::clear() -> void {
  82. return instance->clear();
  83. }
  84. auto Video::size() -> Size {
  85. Size result;
  86. instance->size(result.width, result.height);
  87. return result;
  88. }
  89. auto Video::acquire(uint width, uint height) -> Acquire {
  90. Acquire result;
  91. if(instance->acquire(result.data, result.pitch, width, height)) return result;
  92. return {};
  93. }
  94. auto Video::release() -> void {
  95. return instance->release();
  96. }
  97. auto Video::output(uint width, uint height) -> void {
  98. return instance->output(width, height);
  99. }
  100. auto Video::poll() -> void {
  101. return instance->poll();
  102. }
  103. //
  104. auto Video::onUpdate(const function<void (uint, uint)>& onUpdate) -> void {
  105. update = onUpdate;
  106. }
  107. auto Video::doUpdate(uint width, uint height) -> void {
  108. if(update) return update(width, height);
  109. }
  110. //
  111. auto Video::create(string driver) -> bool {
  112. self.instance.reset();
  113. if(!driver) driver = optimalDriver();
  114. #if defined(VIDEO_CGL)
  115. if(driver == "OpenGL 3.2") self.instance = new VideoCGL(*this);
  116. #endif
  117. #if defined(VIDEO_DIRECT3D)
  118. if(driver == "Direct3D 9.0") self.instance = new VideoDirect3D(*this);
  119. #endif
  120. #if defined(VIDEO_DIRECTDRAW)
  121. if(driver == "DirectDraw 7.0") self.instance = new VideoDirectDraw(*this);
  122. #endif
  123. #if defined(VIDEO_GDI)
  124. if(driver == "GDI") self.instance = new VideoGDI(*this);
  125. #endif
  126. #if defined(VIDEO_GLX)
  127. if(driver == "OpenGL 3.2") self.instance = new VideoGLX(*this);
  128. #endif
  129. #if defined(VIDEO_GLX2)
  130. if(driver == "OpenGL 2.0") self.instance = new VideoGLX2(*this);
  131. #endif
  132. #if defined(VIDEO_WGL)
  133. if(driver == "OpenGL 3.2") self.instance = new VideoWGL(*this);
  134. #endif
  135. #if defined(VIDEO_XSHM)
  136. if(driver == "XShm") self.instance = new VideoXShm(*this);
  137. #endif
  138. #if defined(VIDEO_XVIDEO)
  139. if(driver == "XVideo") self.instance = new VideoXVideo(*this);
  140. #endif
  141. if(!self.instance) self.instance = new VideoDriver(*this);
  142. return self.instance->create();
  143. }
  144. auto Video::hasDrivers() -> vector<string> {
  145. return {
  146. #if defined(VIDEO_WGL)
  147. "OpenGL 3.2",
  148. #endif
  149. #if defined(VIDEO_DIRECT3D)
  150. "Direct3D 9.0",
  151. #endif
  152. #if defined(VIDEO_DIRECTDRAW)
  153. "DirectDraw 7.0",
  154. #endif
  155. #if defined(VIDEO_GDI)
  156. "GDI",
  157. #endif
  158. #if defined(VIDEO_CGL)
  159. "OpenGL 3.2",
  160. #endif
  161. #if defined(VIDEO_GLX)
  162. "OpenGL 3.2",
  163. #endif
  164. #if defined(VIDEO_GLX2)
  165. "OpenGL 2.0",
  166. #endif
  167. #if defined(VIDEO_XVIDEO)
  168. "XVideo",
  169. #endif
  170. #if defined(VIDEO_XSHM)
  171. "XShm",
  172. #endif
  173. "None"};
  174. }
  175. auto Video::optimalDriver() -> string {
  176. #if defined(VIDEO_WGL)
  177. return "OpenGL 3.2";
  178. #elif defined(VIDEO_DIRECT3D)
  179. return "Direct3D 9.0";
  180. #elif defined(VIDEO_DIRECTDRAW)
  181. return "DirectDraw 7.0";
  182. #elif defined(VIDEO_GDI)
  183. return "GDI";
  184. #elif defined(VIDEO_CGL)
  185. return "OpenGL 3.2";
  186. #elif defined(VIDEO_GLX)
  187. return "OpenGL 3.2";
  188. #elif defined(VIDEO_GLX2)
  189. return "OpenGL 2.0";
  190. #elif defined(VIDEO_XVIDEO)
  191. return "XVideo";
  192. #elif defined(VIDEO_XSHM)
  193. return "XShm";
  194. #else
  195. return "None";
  196. #endif
  197. }
  198. auto Video::safestDriver() -> string {
  199. #if defined(VIDEO_DIRECT3D)
  200. return "Direct3D 9.0";
  201. #elif defined(VIDEO_WGL)
  202. return "OpenGL 3.2";
  203. #elif defined(VIDEO_DIRECTDRAW)
  204. return "DirectDraw 7.0";
  205. #elif defined(VIDEO_GDI)
  206. return "GDI";
  207. #elif defined(VIDEO_CGL)
  208. return "OpenGL 3.2";
  209. #elif defined(VIDEO_XSHM)
  210. return "XShm";
  211. #elif defined(VIDEO_XVIDEO)
  212. return "XVideo";
  213. #elif defined(VIDEO_GLX2)
  214. return "OpenGL 2.0";
  215. #elif defined(VIDEO_GLX)
  216. return "OpenGL 3.2";
  217. #else
  218. return "None";
  219. #endif
  220. }
  221. #if defined(DISPLAY_WINDOWS)
  222. static auto CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL {
  223. vector<Video::Monitor>& monitors = *(vector<Video::Monitor>*)dwData;
  224. MONITORINFOEX mi{};
  225. mi.cbSize = sizeof(MONITORINFOEX);
  226. GetMonitorInfo(hMonitor, &mi);
  227. Video::Monitor monitor;
  228. string deviceName = (const char*)utf8_t(mi.szDevice);
  229. if(deviceName.beginsWith(R"(\\.\DISPLAYV)")) return true; //ignore pseudo-monitors
  230. DISPLAY_DEVICE dd{};
  231. dd.cb = sizeof(DISPLAY_DEVICE);
  232. EnumDisplayDevices(mi.szDevice, 0, &dd, 0);
  233. string displayName = (const char*)utf8_t(dd.DeviceString);
  234. monitor.name = {1 + monitors.size(), ": ", displayName};
  235. monitor.primary = mi.dwFlags & MONITORINFOF_PRIMARY;
  236. monitor.x = lprcMonitor->left;
  237. monitor.y = lprcMonitor->top;
  238. monitor.width = lprcMonitor->right - lprcMonitor->left;
  239. monitor.height = lprcMonitor->bottom - lprcMonitor->top;
  240. monitors.append(monitor);
  241. return true;
  242. }
  243. auto Video::hasMonitors() -> vector<Monitor> {
  244. vector<Monitor> monitors;
  245. EnumDisplayMonitors(nullptr, nullptr, MonitorEnumProc, (LPARAM)&monitors);
  246. vector<Monitor> sorted;
  247. for(auto& monitor : monitors) { if(monitor.primary == 1) sorted.append(monitor); }
  248. for(auto& monitor : monitors) { if(monitor.primary == 0) sorted.append(monitor); }
  249. return sorted;
  250. }
  251. #endif
  252. #if defined(DISPLAY_QUARTZ)
  253. static auto MonitorKeyArrayCallback(const void* key, const void* value, void* context) -> void {
  254. CFArrayAppendValue((CFMutableArrayRef)context, key);
  255. }
  256. auto Video::hasMonitors() -> vector<Monitor> {
  257. vector<Monitor> monitors;
  258. @autoreleasepool {
  259. uint count = [[NSScreen screens] count];
  260. for(uint index : range(count)) {
  261. auto screen = [[NSScreen screens] objectAtIndex:index];
  262. auto rectangle = [screen frame];
  263. Monitor monitor;
  264. monitor.name = {1 + monitors.size(), ": Monitor"}; //fallback in case name lookup fails
  265. monitor.primary = monitors.size() == 0; //on macOS, the primary monitor is always the first monitor.
  266. monitor.x = rectangle.origin.x;
  267. monitor.y = rectangle.origin.y;
  268. monitor.width = rectangle.size.width;
  269. monitor.height = rectangle.size.height;
  270. //getting the name of the monitor on macOS: "Think Different"
  271. auto screenDictionary = [screen deviceDescription];
  272. auto screenID = [screenDictionary objectForKey:@"NSScreenNumber"];
  273. auto displayID = [screenID unsignedIntValue];
  274. auto displayPort = CGDisplayIOServicePort(displayID);
  275. auto dictionary = IODisplayCreateInfoDictionary(displayPort, 0);
  276. CFRetain(dictionary);
  277. if(auto names = (CFDictionaryRef)CFDictionaryGetValue(dictionary, CFSTR(kDisplayProductName))) {
  278. auto languageKeys = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
  279. CFDictionaryApplyFunction(names, MonitorKeyArrayCallback, (void*)languageKeys);
  280. auto orderLanguageKeys = CFBundleCopyPreferredLocalizationsFromArray(languageKeys);
  281. CFRelease(languageKeys);
  282. if(orderLanguageKeys && CFArrayGetCount(orderLanguageKeys)) {
  283. auto languageKey = CFArrayGetValueAtIndex(orderLanguageKeys, 0);
  284. auto localName = CFDictionaryGetValue(names, languageKey);
  285. monitor.name = {1 + monitors.size(), ": ", [(__bridge NSString*)localName UTF8String]};
  286. CFRelease(localName);
  287. }
  288. CFRelease(orderLanguageKeys);
  289. }
  290. CFRelease(dictionary);
  291. monitors.append(monitor);
  292. }
  293. }
  294. return monitors;
  295. }
  296. #endif
  297. #if defined(DISPLAY_XORG)
  298. auto Video::hasMonitors() -> vector<Monitor> {
  299. vector<Monitor> monitors;
  300. auto display = XOpenDisplay(nullptr);
  301. auto screen = DefaultScreen(display);
  302. auto rootWindow = RootWindow(display, screen);
  303. auto resources = XRRGetScreenResourcesCurrent(display, rootWindow);
  304. auto primary = XRRGetOutputPrimary(display, rootWindow);
  305. for(uint index : range(resources->noutput)) {
  306. Monitor monitor;
  307. auto output = XRRGetOutputInfo(display, resources, resources->outputs[index]);
  308. if(output->connection != RR_Connected || output->crtc == None) {
  309. XRRFreeOutputInfo(output);
  310. continue;
  311. }
  312. auto crtc = XRRGetCrtcInfo(display, resources, output->crtc);
  313. monitor.name = {1 + monitors.size(), ": ", output->name}; //fallback name
  314. monitor.primary = false;
  315. for(uint n : range(crtc->noutput)) monitor.primary |= crtc->outputs[n] == primary;
  316. monitor.x = crtc->x;
  317. monitor.y = crtc->y;
  318. monitor.width = crtc->width;
  319. monitor.height = crtc->height;
  320. //Linux: "Think Low-Level"
  321. Atom actualType;
  322. int actualFormat;
  323. unsigned long size;
  324. unsigned long bytesAfter;
  325. unsigned char* data = nullptr;
  326. Atom edid = XInternAtom(display, "EDID", False);
  327. auto property = XRRGetOutputProperty(
  328. display, resources->outputs[index],
  329. edid, 0, 384,
  330. 0, 0, 0, &actualType, &actualFormat, &size, &bytesAfter, &data
  331. );
  332. if(size >= 128) {
  333. string name{" "};
  334. //there are four lights! er ... descriptors. one of them is the monitor name.
  335. if(data[0x39] == 0xfc) memory::copy(name.get(), &data[0x3b], 13);
  336. if(data[0x4b] == 0xfc) memory::copy(name.get(), &data[0x4d], 13);
  337. if(data[0x5d] == 0xfc) memory::copy(name.get(), &data[0x5f], 13);
  338. if(data[0x6f] == 0xfc) memory::copy(name.get(), &data[0x71], 13);
  339. if(name.strip()) { //format: "name\n " -> "name"
  340. monitor.name = {1 + monitors.size(), ": ", name, " [", output->name, "]"};
  341. }
  342. }
  343. XFree(data);
  344. monitors.append(monitor);
  345. XRRFreeCrtcInfo(crtc);
  346. XRRFreeOutputInfo(output);
  347. }
  348. XRRFreeScreenResources(resources);
  349. XCloseDisplay(display);
  350. vector<Monitor> sorted;
  351. for(auto& monitor : monitors) { if(monitor.primary == 1) sorted.append(monitor); }
  352. for(auto& monitor : monitors) { if(monitor.primary == 0) sorted.append(monitor); }
  353. return sorted;
  354. }
  355. #endif
  356. auto Video::monitor(string name) -> Monitor {
  357. auto monitors = Video::hasMonitors();
  358. //try to find by name if possible
  359. for(auto& monitor : monitors) {
  360. if(monitor.name == name) return monitor;
  361. }
  362. //fall back to primary if not found
  363. for(auto& monitor : monitors) {
  364. if(monitor.primary) return monitor;
  365. }
  366. //Video::monitors() should never let this occur
  367. Monitor monitor;
  368. monitor.name = "Primary";
  369. monitor.primary = true;
  370. monitor.x = 0;
  371. monitor.y = 0;
  372. monitor.width = 640;
  373. monitor.height = 480;
  374. return monitor;
  375. }
  376. }