system.win32.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #include "system.h"
  2. #include <shlobj.h>
  3. typedef struct AsyncOp{
  4. BBSyncOp syncOp;
  5. BBObject *syncInfo;
  6. size_t asyncRet;
  7. BBAsyncOp asyncOp;
  8. int asyncInfo;
  9. }AsyncOp;
  10. // track shift key state because Windows doesn't report events when the two shift
  11. // keys are being pressed at the same time, and then one released.
  12. static int shiftKeyState[2];
  13. static int _usew;
  14. static HHOOK msghook;
  15. static int mainThreadId;
  16. static int mods;
  17. static int started;
  18. static HWND mouseHwnd;
  19. static BBObject *mouseSource;
  20. static int mouseVisible;
  21. static int keyCode( WPARAM wp,LPARAM lp){
  22. switch( ((lp>>17)&0x80)|((lp>>16)&0x7f) ){
  23. case 42:return VK_LSHIFT;
  24. case 54:return VK_RSHIFT;
  25. case 29:return VK_LCONTROL;
  26. case 157:return VK_RCONTROL;
  27. case 56:return VK_LMENU;
  28. case 184:return VK_RMENU;
  29. }
  30. return wp;
  31. }
  32. static void updateMods(){
  33. mods=0;
  34. if( GetKeyState( VK_SHIFT )<0 ) mods|=MODIFIER_SHIFT;
  35. if( GetKeyState( VK_CONTROL )<0 ) mods|=MODIFIER_CONTROL;
  36. if( GetKeyState( VK_MENU )<0 ) mods|=MODIFIER_OPTION;
  37. if( GetKeyState( VK_LWIN )<0 || GetKeyState( VK_RWIN )<0 ) mods|=MODIFIER_SYSTEM;
  38. }
  39. static void bbSystemShutdown(){
  40. if( !started ) return;
  41. timeEndPeriod( 1 );
  42. started=0;
  43. }
  44. static int isControl( HWND hwnd ){
  45. int style=GetWindowLong( hwnd,GWL_STYLE) & (WS_TABSTOP|WS_CHILD);
  46. return style==(WS_TABSTOP|WS_CHILD);
  47. }
  48. static void updateMouseVisibility(){
  49. int visible=mouseVisible || !mouseHwnd;
  50. int n=ShowCursor( visible );
  51. if( n<-1 || n>0 ) ShowCursor( !visible );
  52. }
  53. static void setMouseHwnd( HWND hwnd,int x,int y,BBObject *source ){
  54. if( hwnd==mouseHwnd ) return;
  55. if( hwnd && source ){
  56. BBRETAIN( source );
  57. }
  58. if( mouseHwnd ){
  59. POINT p;
  60. GetCursorPos( &p );
  61. ScreenToClient( mouseHwnd,&p );
  62. bbSystemEmitEvent( BBEVENT_MOUSELEAVE,mouseSource,0,0,p.x,p.y,&bbNullObject );
  63. if( mouseSource ){
  64. BBRELEASE( mouseSource );
  65. }
  66. }
  67. mouseHwnd=hwnd;
  68. updateMouseVisibility();
  69. if( mouseHwnd ){
  70. TRACKMOUSEEVENT tm={sizeof(tm),TME_LEAVE,hwnd,0};
  71. mouseSource=source;
  72. bbSystemEmitEvent( BBEVENT_MOUSEENTER,mouseSource,0,0,x,y,&bbNullObject );
  73. _TrackMouseEvent( &tm );
  74. }
  75. }
  76. static HWND focHwnd;
  77. static void beginPanel(){
  78. focHwnd=GetFocus();
  79. }
  80. static void endPanel(){
  81. SetFocus( focHwnd );
  82. }
  83. static LRESULT CALLBACK getMessageHook( int code,WPARAM wp,LPARAM lp ){
  84. if( code>=0 && wp==PM_REMOVE ){
  85. MSG *msg=(MSG*)lp;
  86. if( msg->message==WM_BBRESERVED1 ){
  87. AsyncOp *p=(AsyncOp*)msg->lParam;
  88. p->syncOp( p->syncInfo,p->asyncRet );
  89. if( p->asyncOp ){
  90. BBRELEASE( p->syncInfo );
  91. }
  92. free( p );
  93. }
  94. }
  95. return CallNextHookEx( msghook,code,wp,lp );
  96. }
  97. void bbSystemEmitOSEvent( HWND hwnd,UINT msg,WPARAM wp,LPARAM lp,BBObject *source ){
  98. RECT rect;
  99. POINT point;
  100. int inRect,id,data=0,x=0,y=0;
  101. switch( msg ){
  102. case WM_KEYDOWN:case WM_SYSKEYDOWN:
  103. if( wp<1 || wp>255 ) return;
  104. id=( lp & 0x40000000 ) ? BBEVENT_KEYREPEAT : BBEVENT_KEYDOWN;
  105. data=keyCode( wp,lp );
  106. switch(data) {
  107. case VK_LSHIFT:
  108. shiftKeyState[0] = 1;
  109. break;
  110. case VK_RSHIFT:
  111. shiftKeyState[1] = 1;
  112. break;
  113. }
  114. break;
  115. case WM_KEYUP:case WM_SYSKEYUP:
  116. if( wp<1 || wp>255 ) return;
  117. id=BBEVENT_KEYUP;
  118. data=keyCode( wp,lp );
  119. switch(data) {
  120. case VK_LSHIFT:
  121. shiftKeyState[0] = 0;
  122. break;
  123. case VK_RSHIFT:
  124. shiftKeyState[1] = 0;
  125. break;
  126. }
  127. break;
  128. case WM_CHAR:case WM_SYSCHAR:
  129. id=BBEVENT_KEYCHAR;
  130. data=wp;
  131. break;
  132. case WM_LBUTTONDOWN:case WM_RBUTTONDOWN:case WM_MBUTTONDOWN:case WM_XBUTTONDOWN:
  133. SetCapture( hwnd );
  134. id=BBEVENT_MOUSEDOWN;
  135. switch (msg) {
  136. case WM_LBUTTONDOWN:
  137. data = 1;
  138. break;
  139. case WM_RBUTTONDOWN:
  140. data = 2;
  141. break;
  142. case WM_MBUTTONDOWN:
  143. data = 3;
  144. break;
  145. case WM_XBUTTONDOWN:
  146. if (GET_XBUTTON_WPARAM(wp) == XBUTTON1) {
  147. data = 4;
  148. } else {
  149. data = 5;
  150. }
  151. }
  152. x=(short)LOWORD(lp);
  153. y=(short)HIWORD(lp);
  154. break;
  155. case WM_LBUTTONUP:case WM_RBUTTONUP:case WM_MBUTTONUP:case WM_XBUTTONUP:
  156. ReleaseCapture();
  157. id=BBEVENT_MOUSEUP;
  158. switch (msg) {
  159. case WM_LBUTTONUP:
  160. data = 1;
  161. break;
  162. case WM_RBUTTONUP:
  163. data = 2;
  164. break;
  165. case WM_MBUTTONUP:
  166. data = 3;
  167. break;
  168. case WM_XBUTTONUP:
  169. if (GET_XBUTTON_WPARAM(wp) == XBUTTON1) {
  170. data = 4;
  171. } else {
  172. data = 5;
  173. }
  174. }
  175. x=(short)LOWORD(lp);
  176. y=(short)HIWORD(lp);
  177. break;
  178. case WM_MOUSEMOVE:
  179. x=(short)LOWORD(lp);
  180. y=(short)HIWORD(lp);
  181. if (wp&MK_LBUTTON) data=1;
  182. if (wp&MK_MBUTTON) data=4;
  183. if (wp&MK_RBUTTON) data=2;
  184. GetClientRect( hwnd,&rect );
  185. inRect=(x>=0 && y>=0 && x<rect.right && y<rect.bottom);
  186. setMouseHwnd( inRect ? hwnd : 0,x,y,source );
  187. id=BBEVENT_MOUSEMOVE;
  188. break;
  189. case WM_MOUSELEAVE:
  190. if( hwnd==mouseHwnd ) setMouseHwnd( 0,(short)LOWORD(lp),(short)HIWORD(lp),&bbNullObject );
  191. return;
  192. case WM_MOUSEWHEEL:
  193. id=BBEVENT_MOUSEWHEEL;
  194. data=(short)HIWORD(wp)/120;
  195. point.x=(short)LOWORD(lp);
  196. point.y=(short)HIWORD(lp);
  197. ScreenToClient( hwnd,&point );
  198. x=point.x;
  199. y=point.y;
  200. break;
  201. case WM_CLOSE:
  202. id=BBEVENT_APPTERMINATE;
  203. break;
  204. case WM_ACTIVATE:
  205. if( LOWORD(wp)==WA_INACTIVE || !IsIconic(hwnd) ){
  206. DWORD proc;
  207. GetWindowThreadProcessId( hwnd,&proc );
  208. if( proc!=GetCurrentProcessId() ){
  209. id = (LOWORD(wp) == WA_INACTIVE) ? BBEVENT_APPSUSPEND : BBEVENT_APPRESUME;
  210. break;
  211. }
  212. }
  213. return;
  214. /*
  215. case WM_ACTIVATEAPP:
  216. //
  217. // WM_ACTIVATEAPP appears to be broken.
  218. //
  219. // Clicking on taskbar button to minimize an app appears to confuse poor old windows.
  220. //
  221. // So, we'll use the WM_ACTIVATE code above courtesy of Seb...
  222. //
  223. id=wp ? BBEVENT_APPRESUME : BBEVENT_APPSUSPEND;
  224. break;
  225. */
  226. default:
  227. return;
  228. }
  229. bbSystemEmitEvent( id,source,data,mods,x,y,&bbNullObject );
  230. }
  231. void bbSystemStartup(){
  232. OSVERSIONINFO os={ sizeof(os) };
  233. if( started ) return;
  234. if( GetVersionEx( &os ) ){
  235. if( os.dwPlatformId==VER_PLATFORM_WIN32_NT ){
  236. _usew=1;
  237. }
  238. }
  239. mouseVisible=1;
  240. mainThreadId=GetCurrentThreadId();
  241. msghook=SetWindowsHookEx( WH_GETMESSAGE,getMessageHook,0,mainThreadId );
  242. timeBeginPeriod( 1 );
  243. atexit( bbSystemShutdown );
  244. started=1;
  245. }
  246. void bbSystemPoll(){
  247. MSG msg;
  248. while( PeekMessage( &msg,0,0,0,PM_REMOVE ) ){
  249. switch( msg.message ){
  250. case WM_KEYDOWN:case WM_KEYUP:
  251. case WM_SYSKEYDOWN:case WM_SYSKEYUP:
  252. switch( msg.wParam ){
  253. case VK_SHIFT:
  254. case VK_CONTROL:
  255. case VK_MENU:
  256. case VK_LWIN:case VK_RWIN:
  257. updateMods();
  258. break;
  259. }
  260. break;
  261. }
  262. if( isControl( msg.hwnd ) ){
  263. HWND hwnd=GetParent( msg.hwnd );
  264. while( hwnd && isControl( hwnd ) ) hwnd=GetParent( hwnd );
  265. if( hwnd && IsDialogMessage( hwnd,&msg ) ) continue;
  266. }
  267. TranslateMessage( &msg );
  268. DispatchMessage( &msg );
  269. }
  270. // test shift keys current state
  271. if (shiftKeyState[0] && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
  272. shiftKeyState[0] = 0;
  273. bbSystemEmitEvent( BBEVENT_KEYUP,&bbNullObject,VK_LSHIFT,mods,0,0,&bbNullObject );
  274. }
  275. if (shiftKeyState[1] && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
  276. shiftKeyState[1] = 0;
  277. bbSystemEmitEvent( BBEVENT_KEYUP,&bbNullObject,VK_RSHIFT,mods,0,0,&bbNullObject );
  278. }
  279. }
  280. void bbSystemWait(){
  281. MsgWaitForMultipleObjects( 0,0,0,INFINITE,QS_ALLINPUT ); //QS_ALLEVENTS );
  282. bbSystemPoll();
  283. }
  284. void bbSystemMoveMouse( int x,int y ){
  285. POINT point={x,y};
  286. HWND hwnd=GetActiveWindow();
  287. if( hwnd ) ClientToScreen( hwnd,&point );
  288. SetCursorPos( point.x,point.y );
  289. }
  290. void bbSystemSetMouseVisible( int visible ){
  291. mouseVisible=visible;
  292. updateMouseVisibility();
  293. }
  294. static int systemPanel( BBString *text,int flags ){
  295. int n;
  296. beginPanel();
  297. if( _usew ){
  298. BBChar *p=bbStringToWString( text );
  299. BBChar *t=bbStringToWString( bbAppTitle );
  300. n=MessageBoxW( GetActiveWindow(),p,t,flags );
  301. bbMemFree(t);
  302. bbMemFree(p);
  303. }else{
  304. char *p=bbStringToCString( text );
  305. char *t=bbStringToCString( bbAppTitle );
  306. n=MessageBoxA( GetActiveWindow(),p,t,flags );
  307. bbMemFree(t);
  308. bbMemFree(p);
  309. }
  310. endPanel();
  311. return n;
  312. }
  313. void bbSystemNotify( BBString *text,int serious ){
  314. int flags=(serious?MB_ICONWARNING:MB_ICONINFORMATION)|MB_OK|MB_APPLMODAL|MB_TOPMOST;
  315. systemPanel( text,flags );
  316. }
  317. int bbSystemConfirm( BBString *text,int serious ){
  318. int flags=(serious?MB_ICONWARNING:MB_ICONINFORMATION)|MB_OKCANCEL|MB_APPLMODAL|MB_TOPMOST;
  319. int n=systemPanel( text,flags );
  320. if( n==IDOK ) return 1;
  321. return 0;
  322. }
  323. int bbSystemProceed( BBString *text,int serious ){
  324. int flags=(serious?MB_ICONWARNING:MB_ICONINFORMATION)|MB_YESNOCANCEL|MB_APPLMODAL|MB_TOPMOST;
  325. int n=systemPanel( text,flags );
  326. if( n==IDYES ) return 1;
  327. if( n==IDNO ) return 0;
  328. return -1;
  329. }
  330. BBString *bbSystemRequestFile( BBString *text,BBString *exts,int defext,int save,BBString *file,BBString *dir ){
  331. BBString *str=&bbEmptyString;
  332. if( _usew ){
  333. wchar_t buf[MAX_PATH];
  334. OPENFILENAMEW of={sizeof(of)};
  335. BBChar *p=bbStringToWString( file );
  336. wcscpy( buf,p );
  337. bbMemFree(p);
  338. BBChar *t=bbStringToWString( text );
  339. BBChar *e=bbStringToWString( exts );
  340. BBChar *d = 0;
  341. of.hwndOwner=GetActiveWindow();
  342. of.lpstrTitle=t;
  343. of.lpstrFilter=e;
  344. of.nFilterIndex=defext;
  345. of.lpstrFile=buf;
  346. of.lpstrInitialDir=dir->length ? d=bbStringToWString( dir ) : 0;
  347. of.nMaxFile=MAX_PATH;
  348. of.Flags=OFN_HIDEREADONLY|OFN_NOCHANGEDIR;
  349. beginPanel();
  350. if( save ){
  351. of.lpstrDefExt=L"";
  352. of.Flags|=OFN_OVERWRITEPROMPT;
  353. if( GetSaveFileNameW( &of ) ){
  354. str=bbStringFromWString( buf );
  355. }
  356. }else{
  357. of.Flags|=OFN_FILEMUSTEXIST;
  358. if( GetOpenFileNameW( &of ) ){
  359. str=bbStringFromWString( buf );
  360. }
  361. }
  362. endPanel();
  363. bbMemFree(d);
  364. bbMemFree(e);
  365. bbMemFree(t);
  366. }else{
  367. char buf[MAX_PATH];
  368. OPENFILENAMEA of={sizeof(of)};
  369. char *p=bbStringToCString( file );
  370. strcpy( buf,p );
  371. bbMemFree(p);
  372. char *t=bbStringToCString( text );
  373. char *e=bbStringToCString( exts );
  374. char *d=0;
  375. of.hwndOwner=GetActiveWindow();
  376. of.lpstrTitle=t;
  377. of.lpstrFilter=e;
  378. of.nFilterIndex=defext;
  379. of.lpstrFile=buf;
  380. of.lpstrInitialDir=dir->length ? d=bbStringToCString( dir ) : 0;
  381. of.nMaxFile=MAX_PATH;
  382. of.Flags=OFN_HIDEREADONLY|OFN_NOCHANGEDIR;
  383. beginPanel();
  384. if( save ){
  385. of.lpstrDefExt="";
  386. of.Flags|=OFN_OVERWRITEPROMPT;
  387. if( GetSaveFileNameA( &of ) ){
  388. str=bbStringFromCString( buf );
  389. }
  390. }else{
  391. of.Flags|=OFN_FILEMUSTEXIST;
  392. if( GetOpenFileNameA( &of ) ){
  393. str=bbStringFromCString( buf );
  394. }
  395. }
  396. endPanel();
  397. bbMemFree(d);
  398. bbMemFree(e);
  399. bbMemFree(t);
  400. }
  401. return str;
  402. }
  403. static int CALLBACK BrowseForFolderCallbackW( HWND hwnd,UINT uMsg,LPARAM lp,LPARAM pData ){
  404. wchar_t szPath[MAX_PATH];
  405. switch( uMsg ){
  406. case BFFM_INITIALIZED:
  407. SendMessageW( hwnd,BFFM_SETSELECTIONW,TRUE,pData );
  408. break;
  409. case BFFM_SELCHANGED:
  410. if( SHGetPathFromIDListW( (LPITEMIDLIST)lp,szPath ) ){
  411. SendMessageW( hwnd,BFFM_SETSTATUSTEXTW,0,(LPARAM)szPath );
  412. }
  413. break;
  414. }
  415. return 0;
  416. }
  417. static int CALLBACK BrowseForFolderCallbackA( HWND hwnd,UINT uMsg,LPARAM lp,LPARAM pData ){
  418. char szPath[MAX_PATH];
  419. switch( uMsg ){
  420. case BFFM_INITIALIZED:
  421. SendMessageA( hwnd,BFFM_SETSELECTIONA,TRUE,pData );
  422. break;
  423. case BFFM_SELCHANGED:
  424. if( SHGetPathFromIDListA( (LPITEMIDLIST)lp,szPath ) ){
  425. SendMessageA( hwnd,BFFM_SETSTATUSTEXTA,0,(LPARAM)szPath );
  426. }
  427. break;
  428. }
  429. return 0;
  430. }
  431. BBString *bbSystemRequestDir( BBString *text,BBString *dir ){
  432. BBString *str=&bbEmptyString;
  433. if( _usew ){
  434. LPMALLOC shm;
  435. ITEMIDLIST *idlist;
  436. BROWSEINFOW bi={0};
  437. wchar_t buf[MAX_PATH],*p;
  438. BBChar *d=bbStringToWString( dir );
  439. GetFullPathNameW( d,MAX_PATH,buf,&p );
  440. bbMemFree(d);
  441. BBChar *t=bbStringToWString( text );
  442. bi.hwndOwner=GetActiveWindow();
  443. bi.lpszTitle=t;
  444. bi.ulFlags=BIF_RETURNONLYFSDIRS|BIF_NEWDIALOGSTYLE;
  445. bi.lpfn=BrowseForFolderCallbackW;
  446. bi.lParam=(LPARAM)buf;
  447. beginPanel();
  448. idlist=SHBrowseForFolderW(&bi);
  449. endPanel();
  450. bbMemFree(t);
  451. if( idlist ){
  452. SHGetPathFromIDListW( idlist,buf );
  453. str=bbStringFromWString( buf );
  454. //SHFree( idlist ); //?!?
  455. }
  456. } else {
  457. LPMALLOC shm;
  458. ITEMIDLIST *idlist;
  459. BROWSEINFOA bi={0};
  460. char buf[MAX_PATH],*p;
  461. char *d=bbStringToCString( dir );
  462. GetFullPathNameA( d,MAX_PATH,buf,&p );
  463. bbMemFree(d);
  464. char *t=bbStringToCString( text );
  465. bi.hwndOwner=GetActiveWindow();
  466. bi.lpszTitle=t;
  467. bi.ulFlags=BIF_RETURNONLYFSDIRS|BIF_NEWDIALOGSTYLE;
  468. bi.lpfn=BrowseForFolderCallbackA;
  469. bi.lParam=(LPARAM)buf;
  470. beginPanel();
  471. idlist=SHBrowseForFolderA(&bi);
  472. endPanel();
  473. bbMemFree(t);
  474. if( idlist ){
  475. SHGetPathFromIDListA( idlist,buf );
  476. str=bbStringFromCString( buf );
  477. //SHFree( idlist ); //?!?
  478. }
  479. }
  480. return str;
  481. }
  482. int bbOpenURL( BBString *url ){
  483. int n;
  484. if( _usew ){
  485. BBChar *p=bbStringToWString( url );
  486. n=(int)ShellExecuteW( 0,0,(wchar_t*)p,0,0,10 )>32; //SW_SHOWDEFAULT
  487. bbMemFree(p);
  488. }else{
  489. char *p=bbStringToCString( url );
  490. n=(int)ShellExecuteA( 0,0,p,0,0,10 )>32; //SW_SHOWDEFAULT
  491. bbMemFree(p);
  492. }
  493. return n;
  494. }
  495. static DWORD WINAPI asyncOpThread( void *t ){
  496. AsyncOp *p=(AsyncOp*)t;
  497. p->asyncRet=p->asyncOp( p->asyncInfo );
  498. PostThreadMessage( mainThreadId,WM_BBRESERVED1,0,(LPARAM)p );
  499. }
  500. void bbSystemPostSyncOp( BBSyncOp syncOp,BBObject *syncInfo,size_t asyncRet ){
  501. AsyncOp *p=(AsyncOp*)malloc( sizeof( AsyncOp ) );
  502. p->asyncOp=0;
  503. p->asyncRet=asyncRet;
  504. p->syncOp=syncOp;
  505. p->syncInfo=syncInfo;
  506. PostThreadMessage( mainThreadId,WM_BBRESERVED1,0,(LPARAM)p );
  507. }
  508. void bbSystemStartAsyncOp( BBAsyncOp asyncOp,int asyncInfo,BBSyncOp syncOp,BBObject *syncInfo ){
  509. DWORD threadId;
  510. AsyncOp *p=(AsyncOp*)malloc( sizeof( AsyncOp ) );
  511. BBRETAIN( syncInfo );
  512. p->asyncOp=asyncOp;
  513. p->asyncInfo=asyncInfo;
  514. p->syncOp=syncOp;
  515. p->syncInfo=syncInfo;
  516. CreateThread( 0,0,asyncOpThread,p,0,&threadId );
  517. }
  518. int DesktopCaps(int index){
  519. HWND hwnd = GetDesktopWindow();
  520. HDC dc = GetDC(hwnd);
  521. int caps = GetDeviceCaps(dc,index);
  522. ReleaseDC(hwnd,dc);
  523. return caps;
  524. }
  525. int bbSystemDesktopWidth(){
  526. return DesktopCaps(HORZRES);
  527. }
  528. int bbSystemDesktopHeight(){
  529. return DesktopCaps(VERTRES);
  530. }
  531. int bbSystemDesktopDepth(){
  532. return DesktopCaps(BITSPIXEL);
  533. }
  534. int bbSystemDesktopHertz(){
  535. return DesktopCaps(VREFRESH);
  536. }