Windows.cpp 102 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536
  1. /******************************************************************************
  2. On Linux, windows are organized into up to 3 HWND families:
  3. 1. resizable extent (or none if window is not resizable)
  4. 2. borders
  5. 3. client
  6. /******************************************************************************/
  7. #include "stdafx.h"
  8. #if MAC
  9. #include "../Platforms/Mac/MyWindow.h"
  10. #include "../Platforms/Mac/MyOpenGLView.h"
  11. #endif
  12. namespace EE{
  13. /******************************************************************************/
  14. #if WINDOWS_OLD
  15. #include <windowsx.h>
  16. #include <Dbt.h>
  17. #define GET_POINTERID_WPARAM LOWORD
  18. #define WM_POINTERDOWN 0x0246
  19. #define WM_POINTERUP 0x0247
  20. #define WM_POINTERENTER 0x0249
  21. #define WM_POINTERLEAVE 0x024A
  22. #define WM_POINTERUPDATE 0x0245
  23. #define WM_POINTERCAPTURECHANGED 0x024C
  24. #define WM_DPICHANGED 0x02E0
  25. typedef enum tagPOINTER_INPUT_TYPE
  26. {
  27. PT_POINTER=0x00000001,
  28. PT_TOUCH =0x00000002,
  29. PT_PEN =0x00000003,
  30. PT_MOUSE =0x00000004,
  31. }POINTER_INPUT_TYPE;
  32. static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
  33. static HPOWERNOTIFY PowerNotify;
  34. struct WindowCaptureEx
  35. {
  36. HDC dc;
  37. HBITMAP bitmap;
  38. VecI2 size;
  39. WindowCaptureEx() {dc=null; bitmap=null; size.zero();}
  40. ~WindowCaptureEx() {del();}
  41. void del()
  42. {
  43. if(bitmap){DeleteObject(bitmap); bitmap=null;}
  44. if(dc ){DeleteDC (dc ); dc =null;}
  45. size.zero();
  46. }
  47. Bool capture(Image &image, Ptr hwnd=null)
  48. {
  49. Bool ok=false;
  50. #if DX11
  51. if(hwnd==App.hwnd() && SwapChainDesc.SwapEffect!=DXGI_SWAP_EFFECT_DISCARD) // on DX10+ when swap chain flip mode is enabled, using 'GetDC' will result in a black image
  52. {
  53. Renderer.capture(image, -1, -1, -1, -1, 1, false);
  54. }else
  55. #endif
  56. if(HDC src_dc=GetDC((HWND)hwnd))
  57. {
  58. //if(HBITMAP src_bitmap=(HBITMAP)GetCurrentObject(src_dc, OBJ_BITMAP))
  59. {
  60. //BITMAP bmp; if(GetObject(src_bitmap, SIZE(bmp), &bmp))
  61. {
  62. //Int width=bmp.bmWidth, height=bmp.bmHeight; don't use bitmap size because it returns full window size (including borders, not client only)
  63. Int width, height;
  64. if(hwnd)
  65. {
  66. VecI2 size=WindowSize(true, hwnd);
  67. width =size.x;
  68. height=size.y;
  69. }else
  70. {
  71. width =D.screenW();
  72. height=D.screenH();
  73. }
  74. if(width!=size.x || height!=size.y)
  75. {
  76. if(!dc)dc=CreateCompatibleDC(src_dc);
  77. if(bitmap){DeleteObject(bitmap); bitmap=null;}
  78. if(bitmap=CreateCompatibleBitmap(src_dc, width, height)){size.set(width, height); SelectObject(dc, bitmap);}else size.zero();
  79. }
  80. if(bitmap && BitBlt(dc, 0, 0, size.x, size.y, src_dc, 0, 0, SRCCOPY))
  81. {
  82. if(image.hwType()!=IMAGE_B8G8R8A8 || image.size()!=size || image.d()!=1 || image.pitch()!=image.w()*image.bytePP())image.createSoftTry(size.x, size.y, 1, IMAGE_B8G8R8A8);
  83. if(image.is() && image.lock(LOCK_WRITE))
  84. {
  85. BITMAPINFO bi; Zero(bi);
  86. bi.bmiHeader.biSize =SIZE(bi.bmiHeader);
  87. bi.bmiHeader.biWidth = image.w();
  88. bi.bmiHeader.biHeight=-image.h(); // positive height would flip the image
  89. bi.bmiHeader.biPlanes=1;
  90. bi.bmiHeader.biBitCount=32;
  91. bi.bmiHeader.biCompression=BI_RGB;
  92. ok=(GetDIBits(dc, bitmap, 0, image.h(), image.data(), &bi, DIB_RGB_COLORS)>0);
  93. image.unlock();
  94. }
  95. }
  96. }
  97. }
  98. ReleaseDC((HWND)hwnd, src_dc);
  99. }
  100. return ok;
  101. }
  102. };
  103. void WindowCapture::del()
  104. {
  105. Delete((WindowCaptureEx*&)data);
  106. }
  107. Bool WindowCapture::capture(Image &image, Ptr hwnd)
  108. {
  109. WindowCaptureEx* &wc=(WindowCaptureEx*&)data;
  110. if(!wc)New(wc);
  111. return wc->capture(image, hwnd);
  112. }
  113. #else
  114. void WindowCapture::del()
  115. {
  116. }
  117. Bool WindowCapture::capture(Image &image, Ptr hwnd)
  118. {
  119. return false;
  120. }
  121. #endif
  122. /******************************************************************************/
  123. #if WINDOWS_OLD
  124. static ITaskbarList3 *TaskbarList;
  125. void WindowSetText(C Str &text, Ptr hwnd)
  126. {
  127. SetWindowText((HWND)hwnd, text);
  128. }
  129. Str WindowGetText(Ptr hwnd)
  130. {
  131. wchar_t temp[16*1024]; temp[0]='\0'; GetWindowText((HWND)hwnd, temp, Elms(temp)); return temp;
  132. }
  133. void WindowMinimize(Bool force, Ptr hwnd)
  134. {
  135. if(force)ShowWindow((HWND)hwnd, SW_MINIMIZE);
  136. else PostMessage((HWND)hwnd, WM_SYSCOMMAND, SC_MINIMIZE, NULL);
  137. }
  138. void WindowMaximize(Bool force, Ptr hwnd)
  139. {
  140. if(force)ShowWindow((HWND)hwnd, SW_MAXIMIZE);
  141. else PostMessage((HWND)hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, NULL);
  142. }
  143. void WindowReset(Bool force, Ptr hwnd)
  144. {
  145. if(force)ShowWindow((HWND)hwnd, SW_RESTORE);
  146. else PostMessage((HWND)hwnd, WM_SYSCOMMAND, SC_RESTORE, NULL);
  147. }
  148. void WindowToggle(Bool force, Ptr hwnd)
  149. {
  150. if(WindowMaximized(hwnd))WindowReset (force, hwnd);
  151. else WindowMaximize(force, hwnd);
  152. }
  153. void WindowActivate(Ptr hwnd)
  154. {
  155. if(WindowMinimized(hwnd))WindowReset(false, hwnd);
  156. UIntPtr act_thread=GetThreadIdFromWindow(WindowActive()),
  157. cur_thread=GetThreadId ( );
  158. if(cur_thread!=act_thread)AttachThreadInput(act_thread, cur_thread, true);
  159. BringWindowToTop ((HWND)hwnd);
  160. SetForegroundWindow((HWND)hwnd);
  161. if(cur_thread!=act_thread)AttachThreadInput(act_thread, cur_thread, false);
  162. }
  163. void WindowHide(Ptr hwnd)
  164. {
  165. ShowWindow((HWND)hwnd, SW_HIDE);
  166. }
  167. void WindowShow(Ptr hwnd)
  168. {
  169. ShowWindow((HWND)hwnd, SW_SHOWNA);
  170. }
  171. void WindowClose(Ptr hwnd)
  172. {
  173. PostMessage((HWND)hwnd, WM_SYSCOMMAND, SC_CLOSE, NULL);
  174. }
  175. void WindowFlash(Ptr hwnd)
  176. {
  177. FlashWindow((HWND)hwnd, true);
  178. }
  179. /******************************************************************************/
  180. void WindowSetNormal ( Ptr hwnd) {if(TaskbarList) TaskbarList->SetProgressState((HWND)hwnd, TBPF_NOPROGRESS );}
  181. void WindowSetWorking ( Ptr hwnd) {if(TaskbarList) TaskbarList->SetProgressState((HWND)hwnd, TBPF_INDETERMINATE);}
  182. void WindowSetProgress(Flt progress, Ptr hwnd) {if(TaskbarList){TaskbarList->SetProgressState((HWND)hwnd, TBPF_NORMAL ); TaskbarList->SetProgressValue((HWND)hwnd, RoundU(Sat(progress)*65536), 65536);}}
  183. void WindowSetPaused (Flt progress, Ptr hwnd) {if(TaskbarList){TaskbarList->SetProgressState((HWND)hwnd, TBPF_PAUSED ); TaskbarList->SetProgressValue((HWND)hwnd, RoundU(Sat(progress)*65536), 65536);}}
  184. void WindowSetError (Flt progress, Ptr hwnd) {if(TaskbarList){TaskbarList->SetProgressState((HWND)hwnd, TBPF_ERROR ); TaskbarList->SetProgressValue((HWND)hwnd, RoundU(Sat(progress)*65536), 65536);}}
  185. /******************************************************************************/
  186. Byte WindowGetAlpha(Ptr hwnd)
  187. {
  188. BYTE alpha; if(hwnd && GetLayeredWindowAttributes((HWND)hwnd, null, &alpha, null))return alpha;
  189. return 255;
  190. }
  191. void WindowAlpha(Byte alpha, Ptr hwnd)
  192. {
  193. if(hwnd)
  194. {
  195. if(alpha==255)
  196. {
  197. SetWindowLong((HWND)hwnd, GWL_EXSTYLE, GetWindowLong((HWND)hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
  198. }else
  199. {
  200. SetWindowLong((HWND)hwnd, GWL_EXSTYLE, GetWindowLong((HWND)hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
  201. SetLayeredWindowAttributes((HWND)hwnd, 0, alpha, LWA_ALPHA);
  202. }
  203. }
  204. }
  205. /******************************************************************************/
  206. void WindowMove(Int dx, Int dy, Ptr hwnd)
  207. {
  208. if(dx || dy)
  209. {
  210. RECT rect; GetWindowRect((HWND)hwnd, &rect);
  211. MoveWindow((HWND)hwnd, rect.left+dx, rect.top+dy, rect.right-rect.left, rect.bottom-rect.top, true);
  212. }
  213. }
  214. void WindowPos(Int x, Int y, Ptr hwnd)
  215. {
  216. RECT rect; GetWindowRect((HWND)hwnd, &rect);
  217. MoveWindow((HWND)hwnd, x, y, rect.right-rect.left, rect.bottom-rect.top, true);
  218. }
  219. void WindowSize(Int w, Int h, Bool client, Ptr hwnd)
  220. {
  221. RECT rect; GetWindowRect((HWND)hwnd, &rect);
  222. if(client)
  223. {
  224. RECT client; GetClientRect((HWND)hwnd, &client);
  225. w+=(rect.right -rect.left)-(client.right -client.left);
  226. h+=(rect.bottom-rect.top )-(client.bottom-client.top );
  227. }
  228. MoveWindow((HWND)hwnd, rect.left, rect.top, w, h, true);
  229. }
  230. VecI2 WindowSize(Bool client, Ptr hwnd)
  231. {
  232. RECT rect;
  233. if(!client)
  234. {
  235. if(!GetWindowRect((HWND)hwnd, &rect))goto error;
  236. }else
  237. {
  238. if(!GetClientRect((HWND)hwnd, &rect))goto error;
  239. }
  240. return VecI2(rect.right-rect.left, rect.bottom-rect.top);
  241. error:
  242. return 0;
  243. }
  244. RectI WindowRect(Bool client, Ptr hwnd) // !! 'WindowRect' can return weird position when the window is minimized !!
  245. {
  246. RECT rect;
  247. if(!client)
  248. {
  249. if(!GetWindowRect((HWND)hwnd, &rect))goto error;
  250. }else
  251. {
  252. POINT pos={0, 0};
  253. if(!GetClientRect ((HWND)hwnd, &rect))goto error;
  254. ClientToScreen((HWND)hwnd, &pos );
  255. rect.left +=pos.x; rect.top +=pos.y;
  256. rect.right+=pos.x; rect.bottom+=pos.y;
  257. }
  258. #if DEBUG && 0
  259. LogN(S+"WindowRect("+client+")="+RectI(rect.left, rect.top, rect.right, rect.bottom).asText());
  260. #endif
  261. return RectI(rect.left, rect.top, rect.right, rect.bottom);
  262. error:
  263. return RectI(0, 0, 0, 0);
  264. }
  265. Bool WindowMaximized(Ptr hwnd)
  266. {
  267. return IsZoomed((HWND)hwnd)!=0;
  268. }
  269. Bool WindowMinimized(Ptr hwnd)
  270. {
  271. return IsIconic((HWND)hwnd)!=0;
  272. }
  273. void WindowSendData(CPtr data, Int size, Ptr hwnd)
  274. {
  275. if(hwnd && size>=0)
  276. {
  277. COPYDATASTRUCT ds;
  278. ds.dwData=0; // custom ID, not used
  279. ds.lpData=Ptr(data);
  280. ds.cbData=size;
  281. SendMessage((HWND)hwnd, WM_COPYDATA, (WPARAM)App.hwnd(), (LPARAM)&ds);
  282. }
  283. }
  284. /******************************************************************************/
  285. Ptr WindowActive()
  286. {
  287. return (Ptr)GetForegroundWindow();
  288. }
  289. Ptr WindowMouse()
  290. {
  291. POINT cur; if(GetCursorPos(&cur))return (Ptr)WindowFromPoint(cur); return null;
  292. }
  293. Ptr WindowParent(Ptr hwnd)
  294. {
  295. return (Ptr)GetParent((HWND)hwnd);
  296. }
  297. /******************************************************************************/
  298. static BOOL CALLBACK EnumWindowList(HWND hwnd, LPARAM hwnds_ptr)
  299. {
  300. MemPtr<Ptr> &hwnds=*(MemPtr<Ptr>*)hwnds_ptr;
  301. hwnds.add(hwnd);
  302. return true;
  303. }
  304. void WindowList(MemPtr<Ptr> hwnds)
  305. {
  306. hwnds.clear();
  307. EnumWindows(EnumWindowList, LPARAM(&hwnds));
  308. }
  309. /******************************************************************************/
  310. #elif MAC
  311. /******************************************************************************/
  312. void WindowSetText(C Str &text, Ptr hwnd)
  313. {
  314. if(NSWindow *window=(NSWindow*)hwnd)
  315. if(NSStringAuto string=text)
  316. [window setTitle:string];
  317. }
  318. Str WindowGetText(Ptr hwnd)
  319. {
  320. if(NSWindow *window=(NSWindow*)hwnd)return [window title]; // do not release [window title] as it will crash
  321. return S;
  322. }
  323. void WindowActivate(Ptr hwnd)
  324. {
  325. if(NSWindow *window=(NSWindow*)hwnd)
  326. if(window==App.hwnd())
  327. {
  328. ProcessSerialNumber psn;
  329. if(GetProcessForPID(App.processID(), &psn)==noErr)SetFrontProcess(&psn);
  330. }
  331. }
  332. Ptr WindowMouse()
  333. {
  334. return Ms._on_client ? App.hwnd() : null;
  335. }
  336. Bool WindowMaximized(Ptr hwnd)
  337. {
  338. if(NSWindow *window=(NSWindow*)hwnd)return [window isZoomed];
  339. return false;
  340. }
  341. Bool WindowMinimized(Ptr hwnd)
  342. {
  343. if(NSWindow *window=(NSWindow*)hwnd)return [window isMiniaturized];
  344. return false;
  345. }
  346. void WindowClose(Ptr hwnd)
  347. {
  348. if(NSWindow *window=(NSWindow*)hwnd)[window performClose:NSApp];
  349. }
  350. void WindowMinimize(Bool force, Ptr hwnd)
  351. {
  352. if(NSWindow *window=(NSWindow*)hwnd)[window performMiniaturize:NSApp];
  353. }
  354. void WindowMaximize(Bool force, Ptr hwnd)
  355. {
  356. if(NSWindow *window=(NSWindow*)hwnd)
  357. {
  358. if( [window isMiniaturized])WindowActivate(hwnd);
  359. if(![window isZoomed])[window performZoom:NSApp];
  360. }
  361. }
  362. void WindowReset(Bool force, Ptr hwnd)
  363. {
  364. if(NSWindow *window=(NSWindow*)hwnd)
  365. {
  366. if([window isMiniaturized])WindowActivate(hwnd);else
  367. if([window isZoomed])[window performZoom:NSApp];
  368. }
  369. }
  370. void WindowToggle(Bool force, Ptr hwnd)
  371. {
  372. if(NSWindow *window=(NSWindow*)hwnd)
  373. {
  374. if([window isMiniaturized])WindowActivate(hwnd);else
  375. [window performZoom:NSApp];
  376. }
  377. }
  378. void WindowHide(Ptr hwnd)
  379. {
  380. if(NSWindow *window=(NSWindow*)hwnd)
  381. if(window==App.hwnd())[NSApp hide:NSApp];
  382. }
  383. void WindowShow(Ptr hwnd)
  384. {
  385. if(NSWindow *window=(NSWindow*)hwnd)
  386. if(window==App.hwnd())[NSApp unhideWithoutActivation];
  387. }
  388. static VecI2 WindowSize(Bool client, NSWindow *window)
  389. {
  390. NSRect rect=[window frame];
  391. if(client)rect=[window contentRectForFrameRect:rect];
  392. return VecI2(Round(rect.size.width), Round(rect.size.height));
  393. }
  394. static void GetWindowRect(Bool client, NSWindow *window, RectI &r)
  395. {
  396. NSRect rect=[window frame];
  397. if(client)rect=[window contentRectForFrameRect:rect];
  398. r.min.x= Round(rect.origin.x); r.max.x=r.min.x+Round(rect.size.width );
  399. r.max.y=D.screenH()-Round(rect.origin.y); r.min.y=r.max.y-Round(rect.size.height);
  400. }
  401. VecI2 WindowSize(Bool client, Ptr hwnd)
  402. {
  403. if(NSWindow *window=(NSWindow*)hwnd)
  404. {
  405. if(hwnd==App.hwnd() && D.full())return D.res();
  406. return WindowSize(client, window);
  407. }
  408. return 0;
  409. }
  410. RectI WindowRect(Bool client, Ptr hwnd)
  411. {
  412. RectI r;
  413. if(NSWindow *window=(NSWindow*)hwnd)
  414. {
  415. if(hwnd==App.hwnd() && D.full())r.set(0, 0, D.resW(), D.resH());
  416. else GetWindowRect(client, window, r);
  417. }else r.zero();
  418. return r;
  419. }
  420. void WindowMove(Int dx, Int dy, Ptr hwnd)
  421. {
  422. if((dx || dy) && hwnd)
  423. {
  424. RectI r=WindowRect(false, hwnd);
  425. WindowPos(r.min.x+dx, r.min.y+dy, hwnd);
  426. }
  427. }
  428. void WindowPos(Int x, Int y, Ptr hwnd)
  429. {
  430. if(NSWindow *window=(NSWindow*)hwnd)
  431. {
  432. NSPoint p; p.x=x; p.y=D.screenH()-y;
  433. [window setFrameTopLeftPoint:p];
  434. }
  435. }
  436. void WindowSize(Int w, Int h, Bool client, Ptr hwnd)
  437. {
  438. if(NSWindow *window=(NSWindow*)hwnd)
  439. {
  440. // don't use [window setContentSize:NSSize] as it will resize while preserving the bottom left window position
  441. NSRect rect=[window frame];
  442. Flt rect_h=rect.size.height;
  443. rect.size.width =w;
  444. rect.size.height=h;
  445. if(client)
  446. {
  447. #if 1
  448. rect.size.width +=App._bound.w();
  449. rect.size.height+=App._bound.h();
  450. #else
  451. rect.size=[window frameRectForContentRect:rect].size;
  452. #endif
  453. }
  454. rect.origin.y+=rect_h-rect.size.height; // 'origin' is bottom-left window position
  455. [window setFrame:rect display:true]; // 'setFrame' includes borders
  456. }
  457. }
  458. Ptr WindowParent(Ptr hwnd)
  459. {
  460. if(NSWindow *window=(NSWindow*)hwnd)return [window parentWindow];
  461. return null;
  462. }
  463. void WindowFlash(Ptr hwnd)
  464. {
  465. if(App.hwnd()==hwnd) // on Mac OS only our window can be flashed
  466. if(NSApplication *app=NSApp) // get current application id
  467. [app requestUserAttention:NSInformationalRequest];
  468. }
  469. Byte WindowGetAlpha(Ptr hwnd)
  470. {
  471. if(NSWindow *window=(NSWindow*)hwnd)return FltToByte(window.alphaValue);
  472. return 255;
  473. }
  474. void WindowAlpha(Byte alpha, Ptr hwnd)
  475. {
  476. if(NSWindow *window=(NSWindow*)hwnd)[window setAlphaValue:alpha/255.0f];
  477. }
  478. void WindowSendData(CPtr data, Int size, Ptr hwnd) {}
  479. Ptr WindowActive() {return App.active() ? App.hwnd() : null;}
  480. /******************************************************************************/
  481. #elif LINUX
  482. /******************************************************************************/
  483. #define _NET_WM_STATE_REMOVE 0
  484. #define _NET_WM_STATE_ADD 1
  485. #define _NET_WM_STATE_TOGGLE 2
  486. static Atom xdnd_req, WM_PROTOCOLS, WM_DELETE_WINDOW, XdndDrop, XdndActionCopy, XdndPosition, XdndEnter, XdndStatus, XdndTypeList, XdndFinished, XdndSelection, PRIMARY, WM_STATE, _NET_WM_STATE, _NET_WM_STATE_HIDDEN, _NET_WM_STATE_FOCUSED, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_DEMANDS_ATTENTION, _NET_WM_NAME, _NET_FRAME_EXTENTS, UTF8_STRING, _MOTIF_WM_HINTS;
  487. static XWindow xdnd_source;
  488. static long xdnd_version;
  489. static VecI2 xdnd_pos;
  490. static int XInput2Extension;
  491. static Colormap HwndColormap;
  492. static Byte ButtonPressCount[8];
  493. static XIM IM;
  494. static XIC IC;
  495. static Str8 ClassName;
  496. struct MotifWmHints2
  497. {
  498. unsigned long flags;
  499. unsigned long functions;
  500. unsigned long decorations;
  501. long input_mode;
  502. unsigned long status;
  503. };
  504. /******************************************************************************/
  505. void WindowSetText(C Str &text, Ptr hwnd)
  506. {
  507. if(XDisplay && hwnd)
  508. {
  509. Str8 utf=UTF8(text);
  510. XStoreName (XDisplay, XWindow(hwnd), Str8(text));
  511. if(_NET_WM_NAME && UTF8_STRING)XChangeProperty(XDisplay, XWindow(hwnd), _NET_WM_NAME, UTF8_STRING, 8, PropModeReplace, (unsigned char*)utf(), utf.length());
  512. }
  513. }
  514. Str WindowGetText(Ptr hwnd)
  515. {
  516. Str s;
  517. if(XDisplay && hwnd)
  518. {
  519. if(_NET_WM_NAME && UTF8_STRING)
  520. {
  521. Atom type=NULL;
  522. int format=0;
  523. unsigned long items=0, bytes_after=0;
  524. unsigned char *data=null;
  525. if(!XGetWindowProperty(XDisplay, XWindow(hwnd), _NET_WM_NAME, 0, 4096, false, UTF8_STRING, &type, &format, &items, &bytes_after, &data))s=FromUTF8((char*)data);
  526. if(data)XFree(data);
  527. }
  528. if(!s.is())
  529. {
  530. char *name=null; XFetchName(XDisplay, XWindow(hwnd), &name);
  531. if(name){s=name; XFree(name);}
  532. }
  533. }
  534. return s;
  535. }
  536. void WindowActivate(Ptr hwnd)
  537. {
  538. if(XDisplay && hwnd)
  539. {
  540. XRaiseWindow (XDisplay, XWindow(hwnd));
  541. XSetInputFocus(XDisplay, XWindow(hwnd), RevertToParent, CurrentTime);
  542. }
  543. }
  544. Ptr WindowMouse()
  545. {
  546. if(XDisplay)
  547. {
  548. XWindow root, child;
  549. int rx, ry, x, y;
  550. unsigned int mask;
  551. XQueryPointer(XDisplay, DefaultRootWindow(XDisplay), &root, &child, &rx, &ry, &x, &y, &mask);
  552. return Ptr(XmuClientWindow(XDisplay, XWindow(WindowParentTop(Ptr(child)))));
  553. }
  554. return null;
  555. }
  556. Bool WindowMaximized(Ptr hwnd)
  557. {
  558. if(XDisplay && hwnd && _NET_WM_STATE)
  559. {
  560. UInt flags=0;
  561. Atom type=NULL;
  562. int format=0;
  563. unsigned long items=0, bytes_after=0;
  564. unsigned char *data=null;
  565. if(!XGetWindowProperty(XDisplay, XWindow(hwnd), _NET_WM_STATE, 0, 1024, false, XA_ATOM, &type, &format, &items, &bytes_after, &data))
  566. if(Atom *atoms=(Atom*)data)
  567. for(unsigned long i=0; i<items; i++)
  568. {
  569. if(atoms[i]==_NET_WM_STATE_MAXIMIZED_HORZ)flags|=1;else
  570. if(atoms[i]==_NET_WM_STATE_MAXIMIZED_VERT)flags|=2;
  571. }
  572. if(data)XFree(data);
  573. return flags==3;
  574. }
  575. return false;
  576. }
  577. Bool WindowMinimized(Ptr hwnd)
  578. {
  579. if(XDisplay && hwnd && WM_STATE)
  580. {
  581. struct State
  582. {
  583. CARD32 state;
  584. XID icon;
  585. };
  586. bool min=false;
  587. Atom type=NULL;
  588. int format=0;
  589. unsigned long items=0, bytes_after=0;
  590. unsigned char *data=null;
  591. if(!XGetWindowProperty(XDisplay, XWindow(hwnd), WM_STATE, 0, SIZE(State)/4, false, WM_STATE, &type, &format, &items, &bytes_after, &data))
  592. if(State *state=(State*)data)min=(state->state==IconicState);
  593. if(data)XFree(data);
  594. return min;
  595. }
  596. return false;
  597. }
  598. void WindowClose(Ptr hwnd)
  599. {
  600. if(XDisplay && hwnd)XDestroyWindow(XDisplay, XWindow(hwnd));
  601. }
  602. void WindowMinimize(Bool force, Ptr hwnd)
  603. {
  604. if(XDisplay && hwnd)XIconifyWindow(XDisplay, XWindow(hwnd), DefaultScreen(XDisplay));
  605. }
  606. void WindowMaximize(Bool force, Ptr hwnd)
  607. {
  608. if(XDisplay && hwnd && _NET_WM_STATE && _NET_WM_STATE_MAXIMIZED_HORZ && _NET_WM_STATE_MAXIMIZED_VERT)
  609. {
  610. #if 1
  611. XEvent e; Zero(e);
  612. e.xclient.type =ClientMessage;
  613. e.xclient.window =XWindow(hwnd);
  614. e.xclient.message_type=_NET_WM_STATE;
  615. e.xclient.format =32;
  616. e.xclient.data.l[0]=_NET_WM_STATE_ADD;
  617. e.xclient.data.l[1]=_NET_WM_STATE_MAXIMIZED_HORZ;
  618. e.xclient.data.l[2]=_NET_WM_STATE_MAXIMIZED_VERT;
  619. e.xclient.data.l[3]=1;
  620. e.xclient.data.l[4]=0;
  621. XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false, SubstructureRedirectMask|SubstructureNotifyMask, &e);
  622. #else // this doesn't work entirely correctly
  623. Atom type=null;
  624. int format=0;
  625. unsigned long items=0, bytes_after=0;
  626. unsigned char *data=null;
  627. if(!XGetWindowProperty(XDisplay, XWindow(hwnd), _NET_WM_STATE, 0, 1024, false, XA_ATOM, &type, &format, &items, &bytes_after, &data))
  628. {
  629. Atom *atoms=(Atom*)data, temp[1024];
  630. if(items<Elms(temp)-2) // room for _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT
  631. {
  632. UInt flags=0;
  633. for(unsigned long i=0; i<items; i++)
  634. {
  635. temp[i]=atoms[i];
  636. if(temp[i]==_NET_WM_STATE_MAXIMIZED_HORZ)flags|=1;else
  637. if(temp[i]==_NET_WM_STATE_MAXIMIZED_VERT)flags|=2;
  638. }
  639. if(flags!=3)
  640. {
  641. if(!(flags&1))temp[items++]=_NET_WM_STATE_MAXIMIZED_HORZ;
  642. if(!(flags&2))temp[items++]=_NET_WM_STATE_MAXIMIZED_VERT;
  643. XChangeProperty(XDisplay, XWindow(hwnd), _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*)temp, items);
  644. }
  645. }
  646. }
  647. if(data)XFree(data);
  648. XMoveResizeWindow(XDisplay, XWindow(hwnd), 0, 0, App.desktopArea().w(), App.desktopArea().h()); // 'XMoveResizeWindow' accepts client size
  649. #endif
  650. }
  651. }
  652. void WindowReset(Bool force, Ptr hwnd)
  653. {
  654. if(XDisplay && hwnd && _NET_WM_STATE && _NET_WM_STATE_MAXIMIZED_HORZ && _NET_WM_STATE_MAXIMIZED_VERT)
  655. {
  656. #if 1
  657. XEvent e; Zero(e);
  658. e.xclient.type =ClientMessage;
  659. e.xclient.window =XWindow(hwnd);
  660. e.xclient.message_type=_NET_WM_STATE;
  661. e.xclient.format =32;
  662. e.xclient.data.l[0]=_NET_WM_STATE_REMOVE;
  663. e.xclient.data.l[1]=_NET_WM_STATE_MAXIMIZED_HORZ;
  664. e.xclient.data.l[2]=_NET_WM_STATE_MAXIMIZED_VERT;
  665. e.xclient.data.l[3]=1;
  666. e.xclient.data.l[4]=0;
  667. XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false, SubstructureRedirectMask|SubstructureNotifyMask, &e);
  668. #else // this doesn't work entirely correctly
  669. if(hwnd==App.hwnd())
  670. {
  671. Atom type=null;
  672. int format=0;
  673. unsigned long items=0, bytes_after=0;
  674. unsigned char *data=null;
  675. if(!XGetWindowProperty(XDisplay, XWindow(hwnd), _NET_WM_STATE, 0, 1024, false, XA_ATOM, &type, &format, &items, &bytes_after, &data))
  676. {
  677. Atom *atoms=(Atom*)data, temp[1024];
  678. if(items<Elms(temp))
  679. {
  680. unsigned long new_items=0;
  681. for(unsigned long i=0; i<items; i++)
  682. if(atoms[i]!=_NET_WM_STATE_MAXIMIZED_HORZ && atoms[i]!=_NET_WM_STATE_MAXIMIZED_VERT)temp[new_items++]=atoms[i];
  683. if(new_items!=items)XChangeProperty(XDisplay, XWindow(hwnd), _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*)temp, new_items);
  684. }
  685. }
  686. if(data)XFree(data);
  687. XMoveResizeWindow(XDisplay, XWindow(hwnd), App._window_pos.x, App._window_pos.y, App._window_size.x, App._window_size.y); // 'XMoveResizeWindow' accepts client size
  688. }
  689. #endif
  690. }
  691. }
  692. void WindowToggle(Bool force, Ptr hwnd)
  693. {
  694. if(WindowMaximized(hwnd))WindowReset (force, hwnd);
  695. else WindowMaximize(force, hwnd);
  696. }
  697. void WindowHide(Ptr hwnd)
  698. {
  699. if(XDisplay && hwnd)XUnmapWindow(XDisplay, XWindow(hwnd));
  700. }
  701. void WindowShow(Ptr hwnd)
  702. {
  703. if(XDisplay && hwnd)XMapWindow(XDisplay, XWindow(hwnd));
  704. }
  705. VecI2 WindowSize(Bool client, Ptr hwnd)
  706. {
  707. if(XDisplay && hwnd)
  708. {
  709. XWindowAttributes attr; if(XGetWindowAttributes(XDisplay, XWindow(hwnd), &attr)==true)
  710. {
  711. if(!client){attr.width+=App._bound.w(); attr.height+=App._bound.h();}
  712. return VecI2(attr.width, attr.height);
  713. }
  714. }
  715. return 0;
  716. }
  717. RectI WindowRect(Bool client, Ptr hwnd)
  718. {
  719. if(XDisplay && hwnd)
  720. {
  721. XWindowAttributes attr;
  722. #if 0
  723. if(!client)hwnd=WindowParentTop(hwnd); // start straight from the top
  724. #endif
  725. if(XGetWindowAttributes(XDisplay, XWindow(hwnd), &attr)==true)
  726. {
  727. RectI r(attr.x, attr.y, attr.x+attr.width, attr.y+attr.height);
  728. #if 0
  729. if(client) // we want just the client
  730. #endif
  731. for(; hwnd=WindowParent(hwnd); )if(XGetWindowAttributes(XDisplay, XWindow(hwnd), &attr)==true)r+=VecI2(attr.x, attr.y);
  732. #if 1
  733. if(!client){r.min+=App._bound.min; r.max+=App._bound.max;}
  734. #endif
  735. return r;
  736. }
  737. }
  738. return RectI(0, 0, 0, 0);
  739. }
  740. void WindowMove(Int dx, Int dy, Ptr hwnd)
  741. {
  742. if(XDisplay && hwnd && (dx || dy))
  743. {
  744. VecI2 pos=WindowRect(false, hwnd).min;
  745. XMoveWindow(XDisplay, XWindow(hwnd), pos.x+dx, pos.y+dy);
  746. }
  747. }
  748. void WindowPos(Int x, Int y, Ptr hwnd)
  749. {
  750. if(XDisplay && hwnd)XMoveWindow(XDisplay, XWindow(hwnd), x, y);
  751. }
  752. void WindowSize(Int w, Int h, Bool client, Ptr hwnd)
  753. {
  754. if(XDisplay && hwnd)
  755. {
  756. if(!client)
  757. {
  758. #if 1
  759. VecI2 border=App._bound.size();
  760. #else
  761. VecI2 border=WindowSize(false, hwnd)-WindowSize(true, hwnd);
  762. #endif
  763. w-=border.x;
  764. h-=border.y;
  765. }
  766. XResizeWindow(XDisplay, XWindow(hwnd), w, h); // 'XResizeWindow' accepts client size
  767. }
  768. }
  769. Ptr WindowParent(Ptr hwnd)
  770. {
  771. if(XDisplay && hwnd)
  772. {
  773. XWindow root=NULL, parent=NULL, *children=null;
  774. unsigned int nchildren=0;
  775. XQueryTree(XDisplay, XWindow(hwnd), &root, &parent, &children, &nchildren);
  776. if(children)XFree(children);
  777. if(root!=parent)return Ptr(parent);
  778. }
  779. return null;
  780. }
  781. void WindowFlash(Ptr hwnd)
  782. {
  783. if(XDisplay && hwnd && _NET_WM_STATE && _NET_WM_STATE_DEMANDS_ATTENTION)
  784. {
  785. #if 0 // this doesn't work at all
  786. XClientMessageEvent event; Zero(event);
  787. event.type =ClientMessage;
  788. event.message_type=_NET_WM_STATE;
  789. event.display =XDisplay;
  790. event.serial =0;
  791. event.window =XWindow(hwnd);
  792. event.send_event=1;
  793. event.format =32;
  794. event.data.l[0]=_NET_WM_STATE_ADD;
  795. event.data.l[1]=_NET_WM_STATE_DEMANDS_ATTENTION;
  796. XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false, SubstructureRedirectMask|SubstructureNotifyMask, (XEvent*)&event);
  797. #elif 0 // this doesn't work at all
  798. XEvent e; Zero(e);
  799. e.xclient.type =ClientMessage;
  800. e.xclient.window =XWindow(hwnd);
  801. e.xclient.message_type=_NET_WM_STATE;
  802. e.xclient.format =32;
  803. e.xclient.data.l[0]=_NET_WM_STATE_ADD;
  804. e.xclient.data.l[1]=_NET_WM_STATE_DEMANDS_ATTENTION;
  805. e.xclient.data.l[2]=0;
  806. e.xclient.data.l[3]=1;
  807. XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false, SubstructureRedirectMask|SubstructureNotifyMask, &e);
  808. #else // more complex code but works
  809. Atom type=NULL;
  810. int format=0;
  811. unsigned long items=0, bytes_after=0;
  812. unsigned char *data=null;
  813. if(!XGetWindowProperty(XDisplay, XWindow(hwnd), _NET_WM_STATE, 0, 1024, false, XA_ATOM, &type, &format, &items, &bytes_after, &data))
  814. {
  815. Atom *atoms=(Atom*)data, temp[1024];
  816. if(items<Elms(temp)-1) // room for '_NET_WM_STATE_DEMANDS_ATTENTION'
  817. {
  818. bool has=false;
  819. for(unsigned long i=0; i<items; i++)
  820. {
  821. temp[i]=atoms[i];
  822. if(temp[i]==_NET_WM_STATE_DEMANDS_ATTENTION)has=true;
  823. }
  824. if(!has)
  825. {
  826. temp[items++]=_NET_WM_STATE_DEMANDS_ATTENTION;
  827. XChangeProperty(XDisplay, XWindow(hwnd), _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*)temp, items);
  828. }
  829. }
  830. }
  831. if(data)XFree(data);
  832. #endif
  833. }
  834. }
  835. Byte WindowGetAlpha(Ptr hwnd) {return 255;}
  836. void WindowAlpha(Byte alpha, Ptr hwnd) {}
  837. void WindowSendData(CPtr data, Int size, Ptr hwnd) {}
  838. Ptr WindowActive()
  839. {
  840. if(XDisplay)
  841. {
  842. XWindow hwnd; int revert_to;
  843. if(XGetInputFocus(XDisplay, &hwnd, &revert_to)==True)return Ptr(XmuClientWindow(XDisplay, XWindow(WindowParentTop(Ptr(hwnd)))));
  844. }
  845. return App.active() ? App.hwnd() : null;
  846. }
  847. /******************************************************************************/
  848. #else
  849. /******************************************************************************/
  850. void WindowSetText(C Str &text, Ptr hwnd)
  851. {
  852. if(hwnd==App.hwnd())
  853. {
  854. App._name=text;
  855. #if WINDOWS_NEW
  856. Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->Title=ref new Platform::String(text);
  857. #endif
  858. }
  859. }
  860. Str WindowGetText(Ptr hwnd)
  861. {
  862. if(hwnd==App.hwnd())return App.name();
  863. return S;
  864. }
  865. void WindowActivate(Ptr hwnd)
  866. {
  867. }
  868. Ptr WindowMouse()
  869. {
  870. return (App.active() && !App.minimized()) ? App.hwnd() : null;
  871. }
  872. Bool WindowMaximized(Ptr hwnd)
  873. {
  874. return hwnd==App.hwnd() && App.maximized();
  875. }
  876. Bool WindowMinimized(Ptr hwnd)
  877. {
  878. return hwnd==App.hwnd() && App.minimized();
  879. }
  880. void WindowClose(Ptr hwnd)
  881. {
  882. }
  883. void WindowMinimize(Bool force, Ptr hwnd)
  884. {
  885. #if WINDOWS_NEW
  886. //if(hwnd==App.hwnd())Windows::ApplicationModel::Core::CoreApplication::Exit(); this causes a crash, TODO: find alternative
  887. #elif ANDROID
  888. if(hwnd==App.hwnd() && AndroidApp && AndroidApp->activity)ANativeActivity_finish(AndroidApp->activity);
  889. #endif
  890. }
  891. void WindowMaximize(Bool force, Ptr hwnd)
  892. {
  893. }
  894. void WindowReset(Bool force, Ptr hwnd)
  895. {
  896. }
  897. void WindowToggle(Bool force, Ptr hwnd)
  898. {
  899. }
  900. void WindowHide(Ptr hwnd)
  901. {
  902. WindowMinimize(false, hwnd);
  903. }
  904. void WindowShow(Ptr hwnd)
  905. {
  906. WindowReset(false, hwnd);
  907. }
  908. VecI2 WindowSize(Bool client, Ptr hwnd)
  909. {
  910. if(hwnd==App.hwnd())
  911. {
  912. #if WINDOWS_NEW
  913. if(App.hwnd())
  914. {
  915. Windows::Foundation::Rect rect=App.Hwnd()->Bounds; // this returns the client rect
  916. return VecI2(DipsToPixels(rect.Width), DipsToPixels(rect.Height));
  917. }
  918. #endif
  919. return D.res();
  920. }
  921. return 0;
  922. }
  923. RectI WindowRect(Bool client, Ptr hwnd)
  924. {
  925. if(hwnd==App.hwnd())
  926. {
  927. #if WINDOWS_NEW
  928. if(App.hwnd())
  929. {
  930. Windows::Foundation::Rect rect=App.Hwnd()->Bounds; // this returns the client rect
  931. return RectI(DipsToPixels(rect.X), DipsToPixels(rect.Y), DipsToPixels(rect.X+rect.Width), DipsToPixels(rect.Y+rect.Height));
  932. }
  933. #endif
  934. return RectI(0, 0, D.resW(), D.resH());
  935. }
  936. return RectI(0, 0, 0, 0);
  937. }
  938. void WindowMove(Int dx, Int dy, Ptr hwnd)
  939. {
  940. }
  941. void WindowPos(Int x, Int y, Ptr hwnd)
  942. {
  943. }
  944. void WindowSize(Int w, Int h, Bool client, Ptr hwnd)
  945. {
  946. }
  947. Ptr WindowParent(Ptr hwnd)
  948. {
  949. return null;
  950. }
  951. void WindowFlash(Ptr hwnd)
  952. {
  953. }
  954. Byte WindowGetAlpha(Ptr hwnd) {return 255;}
  955. void WindowAlpha(Byte alpha, Ptr hwnd) {}
  956. void WindowSendData(CPtr data, Int size, Ptr hwnd) {}
  957. Ptr WindowActive() {return App.active() ? App.hwnd() : null;}
  958. /******************************************************************************/
  959. #endif
  960. #if WINDOWS_NEW
  961. // TODO: WINDOWS_NEW TaskBar Progress - check this in the future as right now this is not available in UWP
  962. void WindowSetNormal ( Ptr hwnd) {}
  963. void WindowSetWorking ( Ptr hwnd) {}
  964. void WindowSetProgress(Flt progress, Ptr hwnd) {}
  965. void WindowSetPaused (Flt progress, Ptr hwnd) {}
  966. void WindowSetError (Flt progress, Ptr hwnd) {}
  967. #elif !WINDOWS
  968. void WindowSetNormal ( Ptr hwnd) {}
  969. void WindowSetWorking ( Ptr hwnd) {}
  970. void WindowSetProgress(Flt progress, Ptr hwnd) {}
  971. void WindowSetPaused (Flt progress, Ptr hwnd) {}
  972. void WindowSetError (Flt progress, Ptr hwnd) {}
  973. #endif
  974. UInt WindowProc(Ptr hwnd)
  975. {
  976. #if WINDOWS_OLD
  977. DWORD proc=0; GetWindowThreadProcessId((HWND)hwnd, &proc); return proc;
  978. #else
  979. if(hwnd==App.hwnd())return App.processID();
  980. return 0;
  981. #endif
  982. }
  983. #if !WINDOWS_OLD
  984. void WindowList(MemPtr<Ptr> hwnds)
  985. {
  986. if(App.hwnd())hwnds.setNum(1)[0]=App.hwnd();else hwnds.clear();
  987. }
  988. #endif
  989. /******************************************************************************/
  990. Ptr WindowParentTop(Ptr hwnd)
  991. {
  992. for(; Ptr parent=WindowParent(hwnd); )hwnd=parent; return hwnd;
  993. }
  994. /******************************************************************************/
  995. void WindowMsgBox(C Str &title, C Str &text, Bool error)
  996. {
  997. #if WINDOWS_OLD
  998. MessageBox(null, text, title, MB_OK|MB_TOPMOST|(error ? MB_ICONERROR : 0)); // this does not require 'FixNewLine'
  999. #elif WINDOWS_NEW
  1000. if(auto dialog=ref new Windows::UI::Popups::MessageDialog(ref new Platform::String(text), ref new Platform::String(title))) // this does not require 'FixNewLine'
  1001. {
  1002. dialog->Commands->Append(ref new Windows::UI::Popups::UICommand("OK"));
  1003. dialog->ShowAsync();
  1004. }
  1005. #elif LINUX // TODO: what if zenity is not installed?
  1006. Str safe_title= Str(title).replace('`', '\'').replace('"', '\'');
  1007. Str safe_text =XmlString(Str(text ).replace('`', '\''));
  1008. Run("zenity", S+(error ? "--error" : "--info")+" --title=\""+safe_title+"\" --text=\""+safe_text+"\"");
  1009. #elif MAC
  1010. CFStringRef cf_title= CFStringCreateWithCString(kCFAllocatorDefault, title.is() ? UTF8(title)() : "", kCFStringEncodingUTF8); // 'CFUserNotificationDisplayAlert' will freeze if this param is null
  1011. CFStringRef cf_text =(text .is() ? CFStringCreateWithCString(kCFAllocatorDefault, UTF8(text ) , kCFStringEncodingUTF8) : null);
  1012. CFUserNotificationDisplayAlert(0, error ? kCFUserNotificationStopAlertLevel : kCFUserNotificationNoteAlertLevel, null, null, null, cf_title, cf_text, CFSTR("OK"), null, null, null);
  1013. if(cf_title)CFRelease(cf_title);
  1014. if(cf_text )CFRelease(cf_text );
  1015. #elif IOS
  1016. if(NSString *ns_title=AppleString(title)) // have to use 'AppleString' because it will get copied in the local function below
  1017. {
  1018. if(NSString *ns_text=AppleString(text)) // have to use 'AppleString' because it will get copied in the local function below
  1019. {
  1020. dispatch_async(dispatch_get_main_queue(), ^{ // this is needed in case we're calling from a secondary thread
  1021. if(UIAlertController *alert_controller=[UIAlertController alertControllerWithTitle:ns_title message:ns_text preferredStyle:UIAlertControllerStyleAlert])
  1022. {
  1023. [alert_controller addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
  1024. [[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alert_controller animated:YES completion:nil];
  1025. //[alert_controller release]; release will crash
  1026. }
  1027. });
  1028. [ns_text release];
  1029. }
  1030. [ns_title release];
  1031. }
  1032. #elif ANDROID
  1033. // we need to call the code on UI thread, so we need to call java that will do this
  1034. JNI jni;
  1035. if(jni && ActivityClass)
  1036. if(JMethodID messageBox=jni->GetStaticMethodID(ActivityClass, "messageBox", "(Ljava/lang/String;Ljava/lang/String;Z)V"))
  1037. if(JString ti=JString(jni, title))
  1038. if(JString te=JString(jni, text ))
  1039. jni->CallStaticVoidMethod(ActivityClass, messageBox, ti(), te(), jboolean(false));
  1040. #elif WEB
  1041. JavaScriptRun(S+"alert(\""+CString(text)+"\")");
  1042. #endif
  1043. }
  1044. /******************************************************************************/
  1045. #if WINDOWS_OLD
  1046. static void UpdateCandidates() // this function does not remove exising candidates unless it founds new ones (this is because candidates for "q6" keyboard input on QuanPin vista/7 would get cleared)
  1047. {
  1048. Memc<Str> &candidate=(Kb._imm_candidate_hidden ? Kb._imm_candidate_temp : Kb._imm_candidate); candidate.clear();
  1049. Int size=ImmGetCandidateList(Kb._imc, 0, null, 0);
  1050. if( size>0)
  1051. {
  1052. Memt<Byte> temp; CANDIDATELIST *list=(CANDIDATELIST*)temp.setNumZero(size).data(); ImmGetCandidateList(Kb._imc, 0, list, size);
  1053. if(list->dwStyle==IME_CAND_CODE)
  1054. {
  1055. if(list->dwCount==1)candidate.New()=(Char)list->dwOffset[0];else
  1056. if(list->dwCount> 1){} // text representations of individual DBCS character values in hexadecimal notation ?
  1057. }else
  1058. if(list->dwPageSize)
  1059. {
  1060. Int start=Max((Int)list->dwPageStart, (Int)(list->dwSelection/list->dwPageSize*list->dwPageSize) ), // must be aligned to page size
  1061. end =Min((Int)list->dwCount , start+(Int) list->dwPageSize , start+10); // limit to 10 elements
  1062. for(Int i=start; i<end; i++)
  1063. {
  1064. CChar *c=WChar((LPTSTR)(((UIntPtr)list)+list->dwOffset[i]));
  1065. if(Is(c))candidate.add(c);else break;
  1066. }
  1067. }
  1068. }
  1069. }
  1070. static Bool Updating;
  1071. static SByte Activate=-1;
  1072. static Bool NonClientClick=false, ErasedBackground=false;
  1073. enum PAUSE_MODE : Byte
  1074. {
  1075. NOT_PAUSED,
  1076. PAUSED_WAS_INACTIVE,
  1077. PAUSED_WAS_ACTIVE,
  1078. PAUSED_TIMER,
  1079. };
  1080. static PAUSE_MODE PauseMode=NOT_PAUSED;
  1081. static UIntPtr PauseTimer;
  1082. static void Pause(Bool pause)
  1083. {
  1084. if(pause!=(PauseMode!=NOT_PAUSED))switch(PauseMode)
  1085. {
  1086. case NOT_PAUSED: // is not paused, pause now
  1087. {
  1088. if(App.flag&APP_NO_PAUSE_ON_WINDOW_MOVE_SIZE)
  1089. {
  1090. Time.skipUpdate(); PauseTimer=SetTimer(App.Hwnd(), 1, USER_TIMER_MINIMUM, null); PauseMode=PAUSED_TIMER;
  1091. }else
  1092. {
  1093. PauseMode=(App.active() ? PAUSED_WAS_ACTIVE : PAUSED_WAS_INACTIVE); App.setActive(false);
  1094. }
  1095. }break;
  1096. case PAUSED_TIMER: KillTimer(App.Hwnd(), PauseTimer); PauseTimer=0; PauseMode=NOT_PAUSED; break;
  1097. case PAUSED_WAS_ACTIVE : App.setActive(true); // !! no break on purpose !!
  1098. case PAUSED_WAS_INACTIVE: PauseMode=NOT_PAUSED; break;
  1099. }
  1100. }
  1101. static Byte ResetCursorCounter;
  1102. static void ResetCursor() {Ms.resetCursor(); if(ResetCursorCounter){ResetCursorCounter--; App._callbacks.include(ResetCursor);}} // calling immediately may not have any effect, we have to try a few times
  1103. static void ConditionalDraw()
  1104. {
  1105. if(!(App.active() || (App.flag&APP_WORK_IN_BACKGROUND)))DrawState(); // draw only if will not draw by itself
  1106. }
  1107. static LRESULT CALLBACK WindowMsg(HWND hwnd, UInt msg, WPARAM wParam, LPARAM lParam)
  1108. {
  1109. #if 0
  1110. switch(msg)
  1111. {
  1112. case WM_KEYDOWN:
  1113. case WM_KEYUP:
  1114. case WM_MOUSEMOVE:
  1115. case WM_MBUTTONUP:
  1116. case WM_MOUSEWHEEL:
  1117. case WM_NCPAINT:
  1118. case WM_NCHITTEST:
  1119. //case WM_NCCALCSIZE:
  1120. case WM_ERASEBKGND:
  1121. case WM_SETCURSOR:
  1122. case WM_GETMINMAXINFO:
  1123. case WM_PAINT:
  1124. case WM_GETICON:
  1125. case WM_GETTEXT:
  1126. case WM_GETOBJECT: break;
  1127. default:
  1128. {
  1129. Str s=S+"frame:"+Time.frame()+", WM_";
  1130. switch(msg)
  1131. {
  1132. case WM_POWERBROADCAST: s+=S+"POWERBROADCAST:"+wParam; break;
  1133. case WM_DISPLAYCHANGE: s+=S+"DISPLAYCHANGE:"+LOWORD(lParam)+'x'+HIWORD(lParam); break;
  1134. case WM_ACTIVATE: s+=S+"ACTIVATE:"+(wParam==WA_ACTIVE || wParam==WA_CLICKACTIVE); break;
  1135. case WM_NCACTIVATE: s+=S+"NCACTIVATE"; break;
  1136. case WM_ACTIVATEAPP: s+=S+"ACTIVATEAPP"; break;
  1137. case WM_MOVE: s+=S+"MOVE:"+(short)LOWORD(lParam)+','+(short)HIWORD(lParam); break;
  1138. case WM_SIZE: s+=S+"SIZE:"+LOWORD(lParam)+','+HIWORD(lParam); break;
  1139. case WM_NCHITTEST: s+=S+"NCHITTEST"; break;
  1140. case WM_NCCALCSIZE: s+=S+"NCCALCSIZE"; break;
  1141. case WM_SETFOCUS: s+=S+"SETFOCUS"; break;
  1142. case WM_KILLFOCUS: s+=S+"KILLFOCUS"; break;
  1143. case WM_DWMNCRENDERINGCHANGED: s+=S+"DWMNCRENDERINGCHANGED"; break;
  1144. case WM_SETTINGCHANGE:
  1145. {
  1146. s+=S+"SETTINGCHANGE: SPI_";
  1147. switch(wParam)
  1148. {
  1149. default: s+=TextHex(wParam); break;
  1150. case SPI_SETWORKAREA: {RECT rect; SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0); s+=S+"SETWORKAREA:"+rect.left+','+rect.top+','+rect.right+','+rect.bottom;} break;
  1151. }
  1152. }break;
  1153. case WM_WINDOWPOSCHANGING: {WINDOWPOS &wp=*(WINDOWPOS*)lParam; s+=S+"WINDOWPOSCHANGING:"+wp.x+','+wp.y+' '+wp.cx+','+wp.cy;} break;
  1154. case WM_WINDOWPOSCHANGED : {WINDOWPOS &wp=*(WINDOWPOS*)lParam; s+=S+"WINDOWPOSCHANGED:" +wp.x+','+wp.y+' '+wp.cx+','+wp.cy;} break;
  1155. case WM_STYLECHANGING: s+=S+"STYLECHANGING"; break;
  1156. case WM_STYLECHANGED: s+=S+"STYLECHANGED"; break;
  1157. default: s+=TextHex(msg)+", wParam:"+TextHex(wParam)+", lParam:"+TextHex((ULong)lParam); break;
  1158. }
  1159. LogN(s);
  1160. }break;
  1161. }
  1162. #endif
  1163. switch(msg)
  1164. {
  1165. // TIMER
  1166. case WM_TIMER: if(wParam==PauseTimer && !App._close && !Updating)App.update(); break; // allows app updating while window moving/resizing (don't do this if app requested close or it's inside update already, this can happen if in the app we've displayed a system message box making the app paused, and then moving the window, in that case WM_TIMER gets called on the same thread, however inside the messagebox call)
  1167. // APPLICATION
  1168. case WM_CLOSE: if(!(App.flag&APP_NO_CLOSE))App.close(); return 0;
  1169. /*case WM_ACTIVATEAPP: // !! Warning: when clicking on the apps bar in the taskbar to minimize it, WM_ACTIVATEAPP gets called with "wParam==1", but when clicking on the apps bar in the taskbar to activate it, WM_ACTIVATEAPP does not get called !! that's why WM_ACTIVATEAPP is ignored and only WM_ACTIVATE gets checked
  1170. {
  1171. Bool active=(wParam!=0); // 'wParam' can be 0 or 1
  1172. App.setActive(active);
  1173. //LogN(S+"WM_ACTIVATEAPP, active:"+active+", wParam:"+wParam);
  1174. }break;*/
  1175. case WM_ACTIVATE: // !! Warning: when clicking on the apps bar in the taskbar to minimize it, WM_ACTIVATE gets called with "LOWORD(wParam)!=WA_INACTIVE" !!
  1176. {
  1177. //Bool active=(LOWORD(wParam)!=WA_INACTIVE), minimized=(HIWORD(wParam)!=0); active&=!minimized; // LOWORD(wParam) can be: WA_INACTIVE, WA_ACTIVE, WA_CLICKACTIVE. HIWORD(wParam)!=0 indicates being minimized
  1178. Activate=(wParam==WA_ACTIVE || wParam==WA_CLICKACTIVE); // it's active only for WA_ACTIVE WA_CLICKACTIVE and when HIWORD(wParam)==0, which is "active && !minimized"
  1179. // instead of activating the app here, we need to wait to check if WM_NCLBUTTONDOWN gets called to determine whether app was activated with a click on the title bar
  1180. }break;
  1181. case WM_INITMENUPOPUP : case WM_ENTERSIZEMOVE: Pause(true ); break;
  1182. case WM_UNINITMENUPOPUP: case WM_EXITSIZEMOVE : Pause(false); break; // WM_EXITSIZEMOVE called when (finished dragging by title bar or resizing by edge/corner, snapped by User), NOT called when (maximized, snapped by OS)
  1183. case WM_MOVE: // called when moved (dragged by title bar, snapped by User/OS, maximized, minimized)
  1184. if(!WindowMinimized(hwnd) && !WindowMaximized(hwnd) && !D.full()) // use 'hwnd' instead of 'App.hwnd' because WM_MOVE is being called while window is being created "_hwnd=CreateWindowEx(..)" and pointer wasn't set yet, need to check for 'WindowMinimized' and 'WindowMaximized' instead of 'App.minimized' and 'App.maximized' because these are not yet available
  1185. {
  1186. //VecI2 client((short)LOWORD(lParam), (short)HIWORD(lParam));
  1187. App._window_pos=WindowRect(false, hwnd).min; // remember window position for later restoring
  1188. }break;
  1189. case WM_SIZE: // called when resized (resized by edge/corner, snapped by User/OS, maximized, minimized)
  1190. {
  1191. App._minimized=(wParam==SIZE_MINIMIZED);
  1192. App._maximized=(wParam==SIZE_MAXIMIZED);
  1193. if(!App.minimized() && !D.full() && D.created())
  1194. {
  1195. App._window_resized.set(LOWORD(lParam), HIWORD(lParam));
  1196. ConditionalDraw(); // draw too because WM_PAINT won't be called when window is getting smaller
  1197. }
  1198. }break;
  1199. case WM_WINDOWPOSCHANGED: // called when moved/resized (dragged by title bar, resized by edge/corner, snapped by User/OS, maximized, minimized)
  1200. Ms.clipUpdate();
  1201. break;
  1202. case WM_DISPLAYCHANGE: if(D.initialized()) // needed only if device already initialized (to skip setting mouse cursor and screen size when initializing)
  1203. {
  1204. ResetCursorCounter=8; App._callbacks.include(ResetCursor); // it was noticed that after changing resolution, Windows will rescale current cursor, to prevent that, we need to reset it, calling immediately may not have any effect, we have to try a few times
  1205. if(auto screen_changed=D.screen_changed)screen_changed(D.w(), D.h()); // if 'D.scale' is set based on current screen resolution, then we may need to adjust it
  1206. }break;
  1207. case WM_SYSCOMMAND: switch(wParam)
  1208. {
  1209. case SC_CLOSE : if(!(App.flag&APP_NO_CLOSE))App.close(); return 0;
  1210. case SC_MONITORPOWER: return 0;
  1211. case SC_SIZE : return 0;
  1212. case SC_MINIMIZE : if(!(App.flag&APP_MINIMIZABLE))return 0; break;
  1213. case SC_MAXIMIZE : if(!(App.flag&APP_MAXIMIZABLE))return 0; break;
  1214. case SC_MOVE :
  1215. case SC_KEYMENU : if(D.full())return 0; break;
  1216. }break;
  1217. // POWER
  1218. case WM_POWERBROADCAST: switch(wParam)
  1219. {
  1220. case PBT_APMQUERYSUSPEND: return App._stay_awake ? BROADCAST_QUERY_DENY : true; // system asks if it's OK to sleep
  1221. case PBT_APMSUSPEND : // suspending
  1222. case PBT_APMRESUMEAUTOMATIC: // resuming
  1223. {
  1224. if(auto sleep=App.sleep)sleep(wParam==PBT_APMSUSPEND); // copy first to avoid multi-thread issues
  1225. }break;
  1226. }break;
  1227. //case WM_DPICHANGED: break;
  1228. // MOUSE, because there can be a case when the Window is activated by System through WM_ACTIVATE, but we don't activate the App due to Ms.exclusive or Ms.clip, then we always need to activate when the user clicks on the client area
  1229. case WM_LBUTTONDOWN: App.setActive(true); Ms.push (0); return 0; case WM_RBUTTONDOWN: Ms.push (1); return 0; case WM_MBUTTONDOWN: Ms.push (2); return 0;
  1230. case WM_LBUTTONUP : Ms.release(0); return 0; case WM_RBUTTONUP : Ms.release(1); return 0; case WM_MBUTTONUP : Ms.release(2); return 0;
  1231. case WM_XBUTTONDOWN: Ms.push ((GET_XBUTTON_WPARAM(wParam)&XBUTTON1) ? 3 : 4); return 0;
  1232. case WM_XBUTTONUP : Ms.release((GET_XBUTTON_WPARAM(wParam)&XBUTTON1) ? 3 : 4); return 0;
  1233. case WM_NCLBUTTONDOWN: // when clicking on the title bar, this will get called before WM_ACTIVATE, but when clicking on minimize/maximize/close, then it will get called after
  1234. {
  1235. //LogN(S+"WM_NCLBUTTONDOWN, frame:"+Time.frame());
  1236. NonClientClick=true;
  1237. Time.skipUpdate(); // pause can occur when holding mouse button over title bar or window buttons for less time than move/size gets activated, thus causing slow down, so call 'skipUpdate'
  1238. }break;
  1239. //case WM_NCLBUTTONUP: break; this is never called
  1240. case WM_SETCURSOR: // this will get called only if the mouse is on the app window, there will be different results if mouse is on client or for example on the title bar
  1241. {
  1242. if(Ms._on_client=(LOWORD(lParam)==HTCLIENT)){Ms.resetVisibility(); return true;} // call this here to make sure that we have correct cursor set, in Mouse.update there's also '_on_client' modification, because this isn't called when mouse goes outside the window, return true only when on client, so for example at the window edge, system resize cursor can be assigned by default functions
  1243. }break;
  1244. #define WM_MOUSEHWHEEL 0x020E
  1245. case WM_MOUSEHWHEEL: Ms._wheel.x+=Flt(GET_WHEEL_DELTA_WPARAM(wParam))/WHEEL_DELTA; break;
  1246. case WM_MOUSEWHEEL : Ms._wheel.y+=Flt(GET_WHEEL_DELTA_WPARAM(wParam))/WHEEL_DELTA; break;
  1247. // KEYBOARD
  1248. // Order of events 0-WM_INPUT, 1-WM_KEYDOWN, 2-WM_CHAR, 3-WM_KEYUP
  1249. case WM_INPUT:
  1250. {
  1251. UINT size=0; GetRawInputData((HRAWINPUT)lParam, RID_INPUT, null, &size, sizeof(RAWINPUTHEADER));
  1252. Memt<Byte> temp; temp.setNum(size);
  1253. if(GetRawInputData((HRAWINPUT)lParam, RID_INPUT, temp.data(), &size, sizeof(RAWINPUTHEADER))==size)
  1254. {
  1255. RAWINPUT &raw=*(RAWINPUT*)temp.data(); switch(raw.header.dwType)
  1256. {
  1257. case RIM_TYPEKEYBOARD:
  1258. {
  1259. KB_KEY key;
  1260. switch(raw.data.keyboard.VKey)
  1261. {
  1262. case VK_CONTROL: if(raw.data.keyboard.Flags&RI_KEY_E0)goto def; key=KB_LCTRL; break; // skip RI_KEY_E0 right control (it's already handled in WM_KEYDOWN)
  1263. case VK_SHIFT : key=((raw.data.keyboard.MakeCode==42) ? KB_LSHIFT : KB_RSHIFT); break; // 42=KB_LSHIFT, 54=KB_RSHIFT
  1264. case 255 : if(raw.data.keyboard.MakeCode==42 && (raw.data.keyboard.Flags&(RI_KEY_E0|RI_KEY_E1))==RI_KEY_E0){key=KB_PRINT; break;} goto def; // detect KB_PRINT because in 'Kb.exclusive', WM_HOTKEY isn't called
  1265. default: goto def;
  1266. }
  1267. if(raw.data.keyboard.Flags&RI_KEY_BREAK)Kb.release(key);else Kb.push(key, raw.data.keyboard.MakeCode);
  1268. return 0;
  1269. }break;
  1270. case RIM_TYPEMOUSE:
  1271. {
  1272. if(raw.data.mouse.usFlags&MOUSE_MOVE_ABSOLUTE)
  1273. {
  1274. }else
  1275. {
  1276. Ms._delta_relative.x+=raw.data.mouse.lLastX;
  1277. Ms._delta_relative.y-=raw.data.mouse.lLastY;
  1278. }
  1279. }break;
  1280. }
  1281. }
  1282. }break;
  1283. case WM_KEYDOWN :
  1284. case WM_SYSKEYDOWN: // SYSKEYDOWN handles Alt+keys
  1285. {
  1286. #if DEBUG
  1287. U16 rep=lParam&0xFFFF;
  1288. Bool ext=(lParam>>24)&1,
  1289. ctx_code=(lParam>>29)&1,
  1290. prev_state=(lParam>>30)&1,
  1291. trans_state=(lParam>>31)&1;
  1292. #endif
  1293. Byte scan_code=(lParam>>16)&0xFF;
  1294. KB_KEY key=KB_KEY(wParam);
  1295. switch(key)
  1296. {
  1297. case KB_CTRL : if(lParam&(1<<24))key=KB_RCTRL;else return 0; break; // can't push KB_LCTRL, because it could be triggered by KB_RALT, just ignore this rely on RawInput/DirectInput
  1298. //case KB_SHIFT: key=((lParam&(1<<24)) ? KB_RSHIFT : KB_LSHIFT); break; this is not working OK, lParam&(1<<24) is always false
  1299. //case KB_SHIFT: key=((scan_code==42 ) ? KB_LSHIFT : KB_RSHIFT); break; 42=KB_LSHIFT, 54=KB_RSHIFT, releasing doesn't work OK
  1300. //case KB_SHIFT: key=(KB_KEY)MapVirtualKey((lParam>>16)&0xFF, MAPVK_VSC_TO_VK_EX); break; releasing doesn't work OK
  1301. case KB_ALT : key=((lParam&(1<<24)) ? KB_RALT : KB_LALT ); break;
  1302. }
  1303. Kb.push(key, scan_code);
  1304. // !! queue characters after push !!
  1305. if(Kb.anyCtrl() && !Kb.anyAlt()) // if Control is on, then WM_CHAR will not be called, so we must add this char here, don't do this with Alt pressed, because if Ctrl+Alt are pressed, then accented characters will get generated (even if it's left Alt)
  1306. {
  1307. if(key>='A' && key<='Z')Kb.queue(Char(key + (Kb.anyShift() ? 0 : 'a'-'A')), scan_code);else
  1308. if(key>='0' && key<='9')Kb.queue(Char(key) , scan_code);
  1309. }
  1310. }return 0;
  1311. case WM_SYSCHAR: // SYSCHAR handles Alt+chars, also disables beep on Alt+key menu sound when keyboard in non exclusive mode
  1312. case WM_CHAR :
  1313. {
  1314. #if DEBUG
  1315. U16 rep=lParam&0xFFFF;
  1316. Bool ext=(lParam>>24)&1,
  1317. ctx_code=(lParam>>29)&1,
  1318. prev_state=(lParam>>30)&1,
  1319. trans_state=(lParam>>31)&1;
  1320. #endif
  1321. Byte scan_code=(lParam>>16)&0xFF;
  1322. #ifdef UNICODE
  1323. Kb.queue((Char)wParam, scan_code);
  1324. #else
  1325. Kb.queue(Char8To16Fast(wParam), scan_code); // we can assume that Str was already initialized
  1326. #endif
  1327. }return 0;
  1328. case WM_KEYUP :
  1329. case WM_SYSKEYUP: // for KB_SHIFT this will be called only if both shifts are released
  1330. {
  1331. #if DEBUG
  1332. Byte scan_code=(lParam>>16)&0xFF;
  1333. #endif
  1334. KB_KEY key=KB_KEY(wParam);
  1335. switch(key)
  1336. {
  1337. case KB_CTRL : if(lParam&(1<<24))key=KB_RCTRL;else
  1338. #if KB_RAW_INPUT
  1339. return 0;
  1340. #else
  1341. if(Kb._special&1){key=KB_LCTRL; FlagDisable(Kb._special, 1);}else return 0; // release LCTRL only if it was locked
  1342. #endif
  1343. break;
  1344. //case KB_SHIFT: key=((lParam&(1<<24)) ? KB_RSHIFT : KB_LSHIFT); break; this is not working OK, lParam&(1<<24) is always false
  1345. //case KB_SHIFT: key=((scan_code==42 ) ? KB_LSHIFT : KB_RSHIFT); break; 42=KB_LSHIFT, 54=KB_RSHIFT, will not be called for one Shift key if other is already pressed
  1346. //case KB_SHIFT: key=(KB_KEY)MapVirtualKey((lParam>>16)&0xFF, MAPVK_VSC_TO_VK_EX); break; will not be called for one Shift key if other is already pressed
  1347. case KB_ALT : key=((lParam&(1<<24)) ? KB_RALT : KB_LALT ); break;
  1348. }
  1349. Kb.release(key);
  1350. }return 0;
  1351. case WM_HOTKEY:
  1352. {
  1353. #if !KB_RAW_INPUT
  1354. if((lParam>>16)==VK_SNAPSHOT)Kb.push(KB_PRINT, -1); // this is needed only for DirectInput non-exclusive mode
  1355. #endif
  1356. }break;
  1357. // IME
  1358. case WM_INPUTLANGCHANGE : Kb.setLayout(); break; // LANG_TYPE((lParam>>16)&0xFF)
  1359. case WM_IME_CHAR : return 0; // don't process by OS
  1360. case WM_IME_KEYUP : if(!D.exclusive())break; return 0; // don't process by OS
  1361. case WM_IME_KEYDOWN : if(!D.exclusive())break; return 0; // don't process by OS
  1362. case WM_IME_SETCONTEXT : if(!D.exclusive())break; lParam=0; break ; // disables drawing of some windows, "lParam=0" disables drawing candidate list by OS, and enables accessing it manually using GetCandidateList
  1363. case WM_IME_REQUEST : if(!D.exclusive())break; return 0; // don't process by OS
  1364. case WM_IME_SELECT : if(!D.exclusive())break; return 0; // don't process by OS
  1365. case WM_IME_COMPOSITIONFULL : if(!D.exclusive())break; return 0; // don't process by OS
  1366. case WM_IME_STARTCOMPOSITION: if(!D.exclusive())break; return 0; // don't process by OS
  1367. case WM_IME_ENDCOMPOSITION : if(!D.exclusive())break; return 0; // don't process by OS
  1368. case WM_IME_COMPOSITION :
  1369. {
  1370. Kb._imm_buffer.clear(); // always 'clear' even for 'reserve' to avoid copying old data in 'setNum'
  1371. Kb._imm_cursor =LOWORD(ImmGetCompositionString(Kb._imc, GCS_CURSORPOS, null, 0));
  1372. Int chars_needed= ImmGetCompositionString(Kb._imc, GCS_COMPSTR , null, 0)/SIZE(Char);
  1373. if( chars_needed>0)
  1374. {
  1375. Kb._imm_buffer.reserve(chars_needed); ImmGetCompositionString(Kb._imc, GCS_COMPSTR, Kb._imm_buffer._d.data(), chars_needed*SIZE(Char));
  1376. Kb._imm_buffer._d [chars_needed]=0;
  1377. Kb._imm_buffer._length=Length(Kb._imm_buffer());
  1378. }
  1379. Kb._imm_selection=-1;
  1380. Int clause_size=ImmGetCompositionStringA(Kb._imc, GCS_COMPCLAUSE, null, 0); // use A because W crashes for GCS_COMPCLAUSE under WinXP
  1381. if( clause_size>0)
  1382. {
  1383. Memc<UInt> clause; clause.setNum(clause_size/SIZE(UInt)); ImmGetCompositionStringA(Kb._imc, GCS_COMPCLAUSE, clause.data(), clause.elms()*clause.elmSize()); // use A because W crashes for GCS_COMPCLAUSE under WinXP
  1384. FREPA(clause)if(clause[i]>=Kb.immCursor())
  1385. {
  1386. Kb._imm_selection=clause[i];
  1387. if(InRange(i+1, clause))Kb._imm_selection.y=clause[i+1];
  1388. break;
  1389. }
  1390. }
  1391. if(lParam&GCS_RESULTSTR)
  1392. {
  1393. Int chars_needed= ImmGetCompositionString(Kb._imc, GCS_RESULTSTR, null , 0)/SIZE(Char);
  1394. if( chars_needed>0){Memt<Char, 8*1024> imm; imm.setNum(chars_needed); ImmGetCompositionString(Kb._imc, GCS_RESULTSTR, imm.data(), chars_needed*SIZE(Char)); FREP(chars_needed)Kb.queue(imm[i], -1);}
  1395. }
  1396. #if SUPPORT_WINDOWS_XP // additional check for WinXP
  1397. if(OSVerNumber().x<=5) // WinXP and below (WinXP is 5.1, Vista is 6.0, Win7 is 6.1, Win8 is 6.2, Win8.1 is 6.3, Win10 is 10)
  1398. UpdateCandidates(); // must be called here as well because of Chinese NeiMa on WinXP (NeiMa doesn't call IMN_CHANGECANDIDATE), however this can't be called for Vista or newer because it causes disappearing of candidates for QuanPin when typing "q6"
  1399. #endif
  1400. if(!D.exclusive())break;
  1401. }return 0; // don't process by OS
  1402. case WM_IME_NOTIFY:
  1403. {
  1404. switch(wParam)
  1405. {
  1406. case IMN_OPENCANDIDATE : if( Kb._imm_candidate_hidden){Kb._imm_candidate_hidden=false; Swap(Kb._imm_candidate_temp, Kb._imm_candidate);} break; // open must "show" candidates
  1407. case IMN_CLOSECANDIDATE : if(!Kb._imm_candidate_hidden){Kb._imm_candidate_hidden=true ; Swap(Kb._imm_candidate_temp, Kb._imm_candidate);} break; // close must "hide" candidates
  1408. case IMN_CHANGECANDIDATE: UpdateCandidates(); break;
  1409. }
  1410. if(!D.exclusive())break;
  1411. }return 0; // don't process by OS
  1412. // TOUCH
  1413. case WM_POINTERDOWN :
  1414. case WM_POINTERENTER:
  1415. {
  1416. POINT point={GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; VecI2 posi(point.x, point.y); ScreenToClient(App.Hwnd(), &point);
  1417. UInt id=GET_POINTERID_WPARAM(wParam);
  1418. CPtr pid=CPtr(id);
  1419. Bool stylus=false; if(GetPointerType){POINTER_INPUT_TYPE type=PT_POINTER; if(GetPointerType(id, &type))if(type==PT_PEN)stylus=true;}
  1420. Vec2 pos=D.windowPixelToScreen(VecI2(point.x, point.y));
  1421. Touch *touch=FindTouchByHandle(pid);
  1422. if( !touch)touch=&Touches.New().init(posi, pos, pid, stylus);else
  1423. {
  1424. touch->_remove=false; // disable 'remove' in case it was enabled (for example the same touch was released in same/previous frame)
  1425. if(msg!=WM_POINTERENTER)touch->reinit(posi, pos); // re-initialize for push (don't do this for hover because it can be called the same frame that release is called, and for release we want to keep the original values)
  1426. }
  1427. if(msg!=WM_POINTERENTER){touch->_state=BS_ON|BS_PUSHED; touch->_force=1;}
  1428. }return 0; // don't process by OS
  1429. case WM_POINTERUPDATE:
  1430. {
  1431. UInt id=GET_POINTERID_WPARAM(wParam);
  1432. CPtr pid=CPtr(id);
  1433. if(Touch *touch=FindTouchByHandle(pid))
  1434. {
  1435. POINT point={GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; VecI2 posi(point.x, point.y); ScreenToClient(App.Hwnd(), &point);
  1436. touch->_deltai+=posi-touch->_posi;
  1437. touch->_posi =posi;
  1438. touch->_pos =D.windowPixelToScreen(VecI2(point.x, point.y));
  1439. }
  1440. }return 0; // don't process by OS
  1441. case WM_POINTERUP :
  1442. case WM_POINTERLEAVE:
  1443. {
  1444. UInt id=GET_POINTERID_WPARAM(wParam);
  1445. CPtr pid=CPtr(id);
  1446. if(Touch *touch=FindTouchByHandle(pid))
  1447. {
  1448. POINT point={GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; VecI2 posi(point.x, point.y); ScreenToClient(App.Hwnd(), &point);
  1449. touch->_deltai+=posi-touch->_posi;
  1450. touch->_posi =posi;
  1451. touch->_pos =D.windowPixelToScreen(VecI2(point.x, point.y));
  1452. touch->_remove =true;
  1453. if(touch->_state&BS_ON) // check for state in case it was manually eaten
  1454. {
  1455. touch->_state|= BS_RELEASED;
  1456. touch->_state&=~BS_ON;
  1457. }
  1458. }
  1459. }return 0; // don't process by OS
  1460. case WM_POINTERCAPTURECHANGED:
  1461. {
  1462. UInt id=GET_POINTERID_WPARAM(wParam);
  1463. CPtr pid=CPtr(id);
  1464. if(Touch *touch=FindTouchByHandle(pid))
  1465. {
  1466. touch->_remove=true;
  1467. if(touch->_state&BS_ON) // check for state in case it was manually eaten
  1468. {
  1469. touch->_state|= BS_RELEASED;
  1470. touch->_state&=~BS_ON;
  1471. }
  1472. }
  1473. }return 0; // don't process by OS
  1474. // DRAW
  1475. case WM_PAINT: ConditionalDraw(); break;
  1476. case WM_ERASEBKGND: if(ErasedBackground)return 0; ErasedBackground=true; break;
  1477. // DROP
  1478. case WM_DROPFILES: if(App.drop)
  1479. {
  1480. HDROP handle=HDROP(wParam);
  1481. wchar_t name[MAX_LONG_PATH];
  1482. Memc<Str> names; for(Int i=0; DragQueryFile(handle, i++, name, Elms(name))>0; )names.add(name);
  1483. POINT point; DragQueryPoint(handle, &point); Vec2 pos=D.windowPixelToScreen(VecI2(point.x, point.y)); // get precise drop position
  1484. DragFinish(handle); // release before the callback
  1485. App.drop(names, Gui.objAtPos(pos), pos);
  1486. if(!D.full() && !(App.flag&APP_WORK_IN_BACKGROUND))DrawState();
  1487. return 0;
  1488. }break;
  1489. // RECEIVE DATA
  1490. case WM_COPYDATA: if(App.receive_data)
  1491. {
  1492. if(COPYDATASTRUCT *ds=(COPYDATASTRUCT*)lParam)App.receive_data(ds->lpData, ds->cbData, (Ptr)wParam);
  1493. }break;
  1494. // DEVICES
  1495. case WM_DEVICECHANGE:
  1496. {
  1497. if(wParam==DBT_DEVICEARRIVAL
  1498. || wParam==DBT_DEVICEREMOVECOMPLETE
  1499. || wParam==DBT_DEVNODES_CHANGED)ListJoypads();
  1500. }break;
  1501. }
  1502. def:
  1503. return DefWindowProc(hwnd, msg, wParam, lParam);
  1504. }
  1505. #endif
  1506. /******************************************************************************/
  1507. #if WINDOWS
  1508. static BOOL CALLBACK EnumResources(HMODULE hModule, LPCWSTR lpType, LPWSTR lpName, LONG_PTR lParam)
  1509. {
  1510. *((C wchar_t**)lParam)=lpName;
  1511. return false;
  1512. }
  1513. static ATOM WindowClass=0;
  1514. #elif LINUX
  1515. struct XProp
  1516. {
  1517. Int format, count;
  1518. Atom type;
  1519. Ptr data;
  1520. };
  1521. static Byte Deactivate=0; // after how many frames to deactivate
  1522. static void ReadProperty(XProp &p, XWindow w, Atom prop)
  1523. {
  1524. unsigned char *ret=null;
  1525. Atom type=NULL;
  1526. int fmt =0;
  1527. unsigned long count=0;
  1528. unsigned long bytes_left=0;
  1529. int bytes_fetch = 0;
  1530. do{
  1531. if(ret){XFree(ret); ret=null;}
  1532. XGetWindowProperty(XDisplay, w, prop, 0, bytes_fetch, false, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
  1533. bytes_fetch+=bytes_left;
  1534. }while(bytes_left);
  1535. p.data =ret;
  1536. p.format=fmt;
  1537. p.count =count;
  1538. p.type =type;
  1539. }
  1540. static Atom PickTarget(Atom list[], int list_count)
  1541. {
  1542. Atom request=NULL;
  1543. for(int i=0; i<list_count && !request; i++)
  1544. {
  1545. char *name=XGetAtomName(XDisplay, list[i]);
  1546. if(Equal(name, "text/uri-list"))request=list[i];
  1547. XFree(name);
  1548. }
  1549. return request;
  1550. }
  1551. static Atom PickTargetFromAtoms(Atom a0, Atom a1, Atom a2)
  1552. {
  1553. int count=0;
  1554. Atom atom[3];
  1555. if(a0)atom[count++]=a0;
  1556. if(a1)atom[count++]=a1;
  1557. if(a2)atom[count++]=a2;
  1558. return PickTarget(atom, count);
  1559. }
  1560. static void GetWindowBounds(XSetWindowAttributes &win_attr, XVisualInfo *vis_info, XWindow root_win)
  1561. {
  1562. if(XDisplay)
  1563. if(Atom FIND_ATOM(_NET_REQUEST_FRAME_EXTENTS))
  1564. if(XWindow hwnd=XCreateWindow(XDisplay, root_win, 0, 0, 256, 256, 0, vis_info->depth, InputOutput, vis_info->visual, CWBackPixmap|CWBorderPixel|CWColormap|CWEventMask, &win_attr))
  1565. {
  1566. XEvent event; Zero(event);
  1567. event.xclient.type =ClientMessage;
  1568. event.xclient.message_type=_NET_REQUEST_FRAME_EXTENTS;
  1569. event.xclient.display =XDisplay;
  1570. event.xclient.window =hwnd;
  1571. event.xclient.format =32;
  1572. XSendEvent(XDisplay, root_win, false, SubstructureRedirectMask|SubstructureNotifyMask, &event);
  1573. XSync(XDisplay, false); // TODO: test on Ubuntu newer than 14.10 if multiple attempts are still needed (14.10 fails always)
  1574. REP(1024) // 1024 attempts
  1575. {
  1576. Atom type=NULL;
  1577. int format=0;
  1578. unsigned long items=0, bytes_after=0;
  1579. unsigned char *data=null;
  1580. if(!XGetWindowProperty(XDisplay, hwnd, _NET_FRAME_EXTENTS, 0, SIZE(unsigned long)*4, false, XA_CARDINAL, &type, &format, &items, &bytes_after, &data))
  1581. if(type==XA_CARDINAL && format==32 && items>=4)if(long *l=(long*)data)
  1582. {
  1583. long left =l[0],
  1584. right =l[1],
  1585. top =l[2],
  1586. bottom=l[3];
  1587. App._bound.set(-left, -top, right, bottom);
  1588. }
  1589. if(data){XFree(data); break;}
  1590. usleep(1);
  1591. }
  1592. XDestroyWindow(XDisplay, hwnd);
  1593. }
  1594. }
  1595. static Bool InitXInput2()
  1596. {
  1597. int event, error;
  1598. if(XQueryExtension(XDisplay, "XInputExtension", &XInput2Extension, &event, &error))
  1599. {
  1600. int major=2, minor=2; // 2.2 for multi touch
  1601. if(!XIQueryVersion(XDisplay, &major, &minor))
  1602. {
  1603. if(Compare(VecI2(major, minor), VecI2(2, 2))>=0) // got the version
  1604. {
  1605. unsigned char mask[3]={0,0,0};
  1606. XIEventMask event_mask; Zero(event_mask);
  1607. event_mask.deviceid=XIAllMasterDevices;
  1608. event_mask.mask_len=SIZE(mask);
  1609. event_mask.mask =mask;
  1610. XISetMask(mask, XI_RawMotion);
  1611. XISetMask(mask, XI_RawButtonPress);
  1612. XISetMask(mask, XI_RawButtonRelease);
  1613. if(!XISelectEvents(XDisplay, DefaultRootWindow(XDisplay), &event_mask, 1))
  1614. {
  1615. return true;
  1616. }
  1617. }
  1618. }
  1619. }
  1620. return false;
  1621. }
  1622. static void SetXInputValues(C Dbl *input, unsigned char *mask, Int mask_len, Dbl *output, Int output_elms)
  1623. {
  1624. const Int max_axis=16;
  1625. Int mask_elms=Min(max_axis, mask_len*8); // bits per byte
  1626. ZeroN(output, output_elms);
  1627. for(int i=0, o=0; i<mask_elms && o<output_elms; i++, o++)if(XIMaskIsSet(mask, i))output[o]=*input++;
  1628. }
  1629. void Application::setWindowFlags(Bool force_resizable)
  1630. {
  1631. if(XDisplay && hwnd() && _MOTIF_WM_HINTS)
  1632. {
  1633. force_resizable|=FlagTest(flag, APP_RESIZABLE);
  1634. MotifWmHints2 hints; Zero(hints);
  1635. hints.flags =MWM_HINTS_FUNCTIONS|MWM_HINTS_DECORATIONS;
  1636. hints.functions =MWM_FUNC_MOVE | ((flag&APP_NO_CLOSE)?0:MWM_FUNC_CLOSE) | ((flag&APP_MINIMIZABLE)?MWM_FUNC_MINIMIZE :0) | ((flag&APP_MAXIMIZABLE)?MWM_FUNC_MAXIMIZE :0) | (force_resizable?MWM_FUNC_RESIZE :0);
  1637. hints.decorations=((flag&APP_NO_TITLE_BAR)?0:MWM_DECOR_BORDER|MWM_DECOR_TITLE) | ((flag&APP_MINIMIZABLE)?MWM_DECOR_MINIMIZE:0) | ((flag&APP_MAXIMIZABLE)?MWM_DECOR_MAXIMIZE:0) | (force_resizable?MWM_DECOR_RESIZEH:0);
  1638. XChangeProperty(XDisplay, Hwnd(), _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, PropModeReplace, (unsigned char*)&hints, SIZE(hints)/4);
  1639. }
  1640. }
  1641. #endif
  1642. void Application::windowCreate()
  1643. {
  1644. if(LogInit)LogN("Application.windowCreate");
  1645. #if WINDOWS_OLD
  1646. Bool want_minmax = FlagTest(flag, APP_MINIMIZABLE|APP_MAXIMIZABLE),
  1647. want_close =!FlagTest(flag, APP_NO_CLOSE ),
  1648. want_buttons=(want_minmax || want_close);
  1649. C wchar_t *icon=null;
  1650. if(!_icon)EnumResourceNames(_hinstance, RT_GROUP_ICON, EnumResources, (LONG_PTR)&icon); // _hinstance must be used, only this supports loading icon from DLL (GetModuleHandle(null) and null didn't work)
  1651. WNDCLASS wc; Zero(wc);
  1652. Str class_name=S+"Esenthel|"+name()+'|'+(Ptr)_hinstance+'|'+DateTime().getUTC().asText()+'|'+Random(); // create a unique class name, this was needed so that 2 apps don't use the same name because some issues could occur
  1653. wc.style =((want_buttons && !want_close) ? CS_NOCLOSE : 0);
  1654. wc.lpfnWndProc =WindowMsg;
  1655. wc.hInstance =_hinstance;
  1656. wc.hIcon =(_icon ? _icon : LoadIcon(_hinstance, icon));
  1657. wc.hCursor =LoadCursor(null, IDC_ARROW);
  1658. wc.hbrBackground=HBRUSH(GetStockObject(BLACK_BRUSH));
  1659. wc.lpszClassName=class_name;
  1660. WindowClass=RegisterClass(&wc);
  1661. _style_window =(WS_VISIBLE | ((flag&APP_NO_TITLE_BAR)?WS_POPUP:WS_CAPTION) | (want_buttons?WS_SYSMENU:0) | ((flag&APP_MINIMIZABLE)?WS_MINIMIZEBOX:0) | ((flag&APP_MAXIMIZABLE)?WS_MAXIMIZEBOX:0) | ((flag&APP_RESIZABLE)?WS_THICKFRAME:0) );
  1662. _style_window_maximized=(WS_VISIBLE | ((flag&APP_NO_TITLE_BAR)?WS_POPUP:WS_CAPTION) | (want_buttons?WS_SYSMENU:0) | ((flag&APP_MINIMIZABLE)?WS_MINIMIZEBOX:0) | ((flag&APP_MAXIMIZABLE)?WS_MAXIMIZEBOX:0) | ((flag&APP_RESIZABLE)?WS_THICKFRAME:0) | WS_MAXIMIZE);
  1663. _style_full =(WS_VISIBLE | ( WS_POPUP ) | (want_buttons?WS_SYSMENU:0) );
  1664. {
  1665. RECT rect;
  1666. Zero(rect); AdjustWindowRect(&rect, _style_window , false); _bound .set(rect.left, rect.top, rect.right, rect.bottom);
  1667. Zero(rect); AdjustWindowRect(&rect, _style_window_maximized, false); _bound_maximized.set(rect.left, rect.top, rect.right, rect.bottom);
  1668. }
  1669. #elif MAC
  1670. // set some initial values, we will change them later after we have bounds
  1671. NSRect rect;
  1672. rect.origin.x =0;
  1673. rect.origin.y =0;
  1674. rect.size.width =512;
  1675. rect.size.height=256;
  1676. UInt style=NSWindowStyleMaskTitled;
  1677. if( flag& APP_MINIMIZABLE )style|=NSWindowStyleMaskMiniaturizable;
  1678. if( flag&(APP_MAXIMIZABLE|APP_RESIZABLE))style|=NSWindowStyleMaskResizable; // on Mac this needs to be enabled if we want to have any of APP_MAXIMIZABLE|APP_RESIZABLE, because without it, the maximize button will be hidden and window will not be resizable around the edges
  1679. if(!(flag& APP_NO_CLOSE) )style|=NSWindowStyleMaskClosable;
  1680. NSWindow *window=[[MyWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO];
  1681. _hwnd=window;
  1682. if(!(flag&APP_MINIMIZABLE))[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:true];
  1683. if(!(flag&APP_MAXIMIZABLE))[[window standardWindowButton:NSWindowZoomButton ] setHidden:true];
  1684. if( flag&APP_NO_CLOSE )[[window standardWindowButton:NSWindowCloseButton ] setHidden:true];
  1685. WindowSetText(name());
  1686. [window setAcceptsMouseMovedEvents:YES];
  1687. // we can calculate bounds only after having a window
  1688. {
  1689. RectI w, c; GetWindowRect(false, window, w); GetWindowRect(true, window, c);
  1690. _bound.set(w.min.x-c.min.x, w.min.y-c.min.y, w.max.x-c.max.x, w.max.y-c.max.y);
  1691. _bound_maximized=_bound;
  1692. }
  1693. #elif LINUX
  1694. if(!XDisplay)return;
  1695. ClassName=S+"Esenthel|"+name()+'|'+DateTime().getUTC().asText()+'|'+Random(); // create a unique class name in case it is needed
  1696. // GL Config
  1697. int attribs[]=
  1698. {
  1699. GLX_X_RENDERABLE , true,
  1700. GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
  1701. GLX_RENDER_TYPE , GLX_RGBA_BIT,
  1702. GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
  1703. GLX_RED_SIZE , 8,
  1704. GLX_GREEN_SIZE , 8,
  1705. GLX_BLUE_SIZE , 8,
  1706. GLX_ALPHA_SIZE , 8,
  1707. GLX_DEPTH_SIZE , 24,
  1708. GLX_STENCIL_SIZE , 8,
  1709. GLX_DOUBLEBUFFER , true,
  1710. NULL
  1711. };
  1712. int count=0; if(GLXFBConfig *fbc=glXChooseFBConfig(XDisplay, DefaultScreen(XDisplay), attribs, &count))
  1713. {
  1714. if(count>=1)GLConfig=fbc[0];
  1715. XFree(fbc);
  1716. }
  1717. if(!GLConfig)Exit("Couldn't find valid GL Config");
  1718. // window
  1719. XVisualInfo *vis_info=glXGetVisualFromFBConfig(XDisplay, GLConfig); if(!vis_info)Exit("Couldn't get XVisualInfo");
  1720. XWindow root_win=RootWindow(XDisplay, DefaultScreen(XDisplay));
  1721. XSetWindowAttributes win_attr; Zero(win_attr);
  1722. win_attr.event_mask=
  1723. StructureNotifyMask // needed for ConfigureNotify, MapNotify
  1724. |KeyPressMask|KeyReleaseMask // needed for KeyPress, KeyRelease
  1725. //|KeymapStateMask // needed for KeymapNotify (currently disabled)
  1726. |ButtonPressMask|ButtonReleaseMask // needed for ButtonPress, ButtonRelease
  1727. |FocusChangeMask // needed for FocusOut, FocusIn
  1728. |EnterWindowMask|LeaveWindowMask // needed for EnterNotify, LeaveNotify
  1729. ; // there are others that could be investigated
  1730. win_attr.background_pixmap=NULL;
  1731. win_attr.background_pixel =0;
  1732. win_attr.border_pixel =0;
  1733. win_attr.colormap =XCreateColormap(XDisplay, root_win, vis_info->visual, AllocNone);
  1734. if(!(HwndColormap=win_attr.colormap))Exit("Can't create ColorMap");
  1735. if(!(flag&APP_NO_TITLE_BAR))
  1736. {
  1737. _bound.set(-1, -38, 1, 1); // estimate first
  1738. GetWindowBounds(win_attr, vis_info, root_win);
  1739. }
  1740. InitXInput2();
  1741. #endif
  1742. #if WINDOWS_NEW
  1743. if(flag&APP_NO_TITLE_BAR)Windows::ApplicationModel::Core::CoreApplication::GetCurrentView()->TitleBar->ExtendViewIntoTitleBar=true;
  1744. _window_size=WindowSize(true); // we can't specify a custom '_window_size' because here the Window has already been created, instead obtain what we've got
  1745. Bool change_size=(D.res()!=_window_size);
  1746. RequestDisplayMode(change_size ? D.resW() : -1, change_size ? D.resH() : -1, (D.full()!=T.Fullscreen()) ? D.full() : -1);
  1747. #elif MOBILE
  1748. D._full=true; // mobile are always fullscreen
  1749. #elif WEB
  1750. D._full=false; // we can never start a WEB app in full mode, it must be entered on user-click only
  1751. Int w=D.resW(), h=D.resH();
  1752. #else
  1753. RectI full, work; VecI2 max_normal_win_client_size, maximized_win_client_size;
  1754. D.curMonitor(full, work, max_normal_win_client_size, maximized_win_client_size);
  1755. Int x=0, y=0, w, h;
  1756. Bool maximize=false;
  1757. _window_size.set(Min(maximized_win_client_size.x, RoundPos(full.w()*0.5f)),
  1758. Min(maximized_win_client_size.y, RoundPos(full.h()*0.5f)));
  1759. if(D.resW()>=full.w() && D.resH()>=full.h())D._full=true;
  1760. if(D.full())
  1761. {
  1762. if(!D.resW())D._res.x=full.w(); w=full.w();
  1763. if(!D.resH())D._res.y=full.h(); h=full.h();
  1764. _window_pos=INT_MAX;
  1765. }else
  1766. {
  1767. if(!D.resW())D._res.x=_window_size.x;
  1768. if(!D.resH())D._res.y=_window_size.y;
  1769. if( D.resW()>=Min(maximized_win_client_size.x, max_normal_win_client_size.x+1)
  1770. && D.resH()>=Min(maximized_win_client_size.y, max_normal_win_client_size.y+1) && !(flag&APP_HIDDEN)) // if exceeds the limits of a normal window (and is not going to be hidden, we can't create hidden maximized window, because maximizing requires separate call to 'WindowMaximize' which shows window)
  1771. {
  1772. maximize=true;
  1773. D._res.x=maximized_win_client_size.x;
  1774. D._res.y=maximized_win_client_size.y;
  1775. }else
  1776. {
  1777. MIN(D._res.x, max_normal_win_client_size.x);
  1778. MIN(D._res.y, max_normal_win_client_size.y);
  1779. _window_size=D.res();
  1780. }
  1781. w=D.resW();
  1782. h=D.resH();
  1783. Int W=w+(maximize ? _bound_maximized.w() : _bound.w()), // width including border
  1784. H=h+(maximize ? _bound_maximized.h() : _bound.h()); // height including border
  1785. #if WINDOWS
  1786. if(maximize) // on Windows when wanting to maximize, create the window initially smaller, so when we un-maximize (by clicking the maximize button) it will set this size, however to get maximized state at the start, after window is created, we will maximize it with 'WindowMaximize'
  1787. { // here we calculate the un-maximized size
  1788. W=_window_size.x+_bound.w(); // width including border
  1789. H=_window_size.y+_bound.h(); // height including border
  1790. }
  1791. // Windows expects the size to include borders
  1792. w=W;
  1793. h=H;
  1794. #endif
  1795. if(T.x<=-1)x=work.min.x+_bound.min.x+1;else if(!T.x)x=work.centerXI()-W/2;else x=work.max.x-W+_bound.max.x-1;
  1796. if(T.y>= 1)y=work.min.y ;else if(!T.y)y=work.centerYI()-H/2;else y=work.max.y-H+_bound.max.y-1;
  1797. #if !WINDOWS // on Windows set the position according to the smaller size (and not the code below), because it will get positioned in 'WindowMaximize'
  1798. if(maximize) // when wanting to create a window maximized, force position to left top corner
  1799. { // on Windows when window is maximized, client X is at 0, so the window position is moved to the left by border width, and we have to move up too
  1800. x=work.min.x+_bound_maximized.min.x; // this sets client X at 0
  1801. y=work.min.y-_bound_maximized.max.y; // move window up by border width (use bottom and not the top, because top includes title bar, it's bigger)
  1802. }
  1803. #endif
  1804. _window_pos.set(x, y);
  1805. }
  1806. #endif
  1807. #if WINDOWS_OLD
  1808. UInt style=(D.full() ? _style_full : maximize ? _style_window_maximized : _style_window); if((flag&APP_HIDDEN) || maximize)FlagDisable(style, WS_VISIBLE); // if maximizing then create as hidden, so small sized window will not be displayed, it will be shown either way because below 'WindowMaximize' shows the window
  1809. _hwnd=(Ptr)CreateWindowEx(drop ? WS_EX_ACCEPTFILES : 0, (LPCWSTR)WindowClass, name(), style, x, y, w, h, null, null, _hinstance, null);
  1810. if(!_hwnd)Exit(MLTC(u"Can't create window", PL,u"Nie można utworzyć okienka"));
  1811. if(maximize)WindowMaximize(true);
  1812. // IMM
  1813. Kb._imc=ImmGetContext(Hwnd());
  1814. Kb. imm(false); // disable by default
  1815. // allow drag and drop when ran as admin (without this, drag and drop won't work for admin)
  1816. if(HMODULE user=GetModuleHandle(L"User32.dll"))
  1817. {
  1818. GetPointerType=(decltype(GetPointerType))GetProcAddress(user, "GetPointerType"); // available on Windows 8+
  1819. #if SUPPORT_WINDOWS_XP
  1820. if(BOOL (WINAPI *ChangeWindowMessageFilter)(UINT message, DWORD dwFlag)=(decltype(ChangeWindowMessageFilter))GetProcAddress(user, "ChangeWindowMessageFilter")) // available on Vista+
  1821. #endif
  1822. {
  1823. #define MSGFLT_ADD 1
  1824. #define WM_COPYGLOBALDATA 0x0049
  1825. ChangeWindowMessageFilter(WM_DROPFILES , MSGFLT_ADD);
  1826. ChangeWindowMessageFilter(WM_COPYDATA , MSGFLT_ADD);
  1827. ChangeWindowMessageFilter(WM_COPYGLOBALDATA, MSGFLT_ADD);
  1828. }
  1829. if(HPOWERNOTIFY (WINAPI *RegisterSuspendResumeNotification)(HANDLE hRecipient, DWORD Flags)=(decltype(RegisterSuspendResumeNotification))GetProcAddress(user, "RegisterSuspendResumeNotification")) // available on Win8+
  1830. PowerNotify=RegisterSuspendResumeNotification(Hwnd(), DEVICE_NOTIFY_WINDOW_HANDLE);
  1831. }
  1832. RegisterHotKey(Hwnd(), 0, 0, VK_SNAPSHOT); // allows KB_PRINT detection through WM_HOTKEY, and disable system shortcut, WM_KEYDOWN does not detect KB_PRINT, however WM_KEYUP does detect it. WM_HOTKEY won't work in KB_RAW_INPUT 'Kb.exclusive', but still call this, in case exclusive==false, to disable system screenshot slow downs
  1833. #elif MAC
  1834. WindowPos (x, y);
  1835. WindowSize(w, h, true);
  1836. // get rect after setting final window size
  1837. rect=[window contentRectForFrameRect:[window frame]];
  1838. OpenGLView=[[MyOpenGLView alloc] initWithFrame:rect];
  1839. NSTrackingArea *tracking_area=[[NSTrackingArea alloc] initWithRect:rect options:(NSTrackingActiveAlways|NSTrackingMouseEnteredAndExited|NSTrackingInVisibleRect) owner:OpenGLView userInfo:nil];
  1840. [OpenGLView addTrackingArea:tracking_area]; // needed for 'mouseEntered, mouseExited'
  1841. [tracking_area release];
  1842. if([OpenGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)])[OpenGLView setWantsBestResolutionOpenGLSurface:YES];
  1843. [window setContentView:OpenGLView];
  1844. [OpenGLView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; // enable drag and drop
  1845. #elif LINUX
  1846. _hwnd=(Ptr)XCreateWindow(XDisplay, root_win, x, y, w, h, 0, vis_info->depth, InputOutput, vis_info->visual, CWBackPixmap|CWBorderPixel|CWColormap|CWEventMask, &win_attr);
  1847. if(!_hwnd)Exit("Can't create window");
  1848. WindowSetText(name());
  1849. XSetWindowBackground(XDisplay, Hwnd(), 0);
  1850. if(FIND_ATOM(WM_PROTOCOLS))
  1851. if(FIND_ATOM(WM_DELETE_WINDOW))XSetWMProtocols(XDisplay, Hwnd(), &WM_DELETE_WINDOW, true); // register custom "close window" callback
  1852. setWindowFlags();
  1853. if(drop)if(Atom FIND_ATOM(XdndAware))
  1854. {
  1855. int xdnd_version=5; XChangeProperty(XDisplay, Hwnd(), XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&xdnd_version, 1);
  1856. }
  1857. FIND_ATOM(XdndDrop);
  1858. FIND_ATOM(XdndActionCopy);
  1859. FIND_ATOM(XdndPosition);
  1860. FIND_ATOM(XdndEnter);
  1861. FIND_ATOM(XdndStatus);
  1862. FIND_ATOM(XdndTypeList);
  1863. FIND_ATOM(XdndFinished);
  1864. FIND_ATOM(XdndSelection);
  1865. FIND_ATOM(PRIMARY);
  1866. CChar8 *class_name=ClassName();
  1867. XWindow hwnd =Hwnd();
  1868. if(IM=XOpenIM(XDisplay, null, (char*)class_name, (char*)class_name))
  1869. IC=XCreateIC(IM, XNClientWindow, hwnd, XNFocusWindow, hwnd, XNInputStyle, XIMPreeditNothing|XIMStatusNothing, XNResourceName, class_name, XNResourceClass, class_name, Ptr(null)); // last parameter must be of Ptr type and not Int
  1870. if(_icon.is())icon(_icon);
  1871. if(!(flag&APP_HIDDEN))XMapWindow(XDisplay, Hwnd()); // display window after everything is ready
  1872. #elif WEB
  1873. Flt zoom=D.browserZoom();
  1874. if(w<=0 || h<=0) // if dimensions haven't been specified
  1875. {
  1876. if(flag&APP_WEB_DISABLE_AUTO_RESIZE)
  1877. {
  1878. Dbl css_w, css_h; emscripten_get_element_css_size(null, &css_w, &css_h); // first try getting from CSS
  1879. w=RoundPos(css_w*zoom); h=RoundPos(css_h*zoom);
  1880. if(w<=0 || h<=0) // if still unavailable
  1881. {
  1882. emscripten_get_canvas_element_size(null, &w, &h);
  1883. if(w<=0 || h<=0){w=800; h=600;} // if still unavailable then use some constant
  1884. }
  1885. }else
  1886. {
  1887. #if 0 // can't use this because the values are always Int's
  1888. w=Max(1, RoundPos(TextFlt(JavaScriptRunS("window.innerWidth" ))*zoom));
  1889. h=Max(1, RoundPos(TextFlt(JavaScriptRunS("window.innerHeight"))*zoom));
  1890. #else
  1891. JavaScriptRun("var target=Module['canvas'];if(target){target.style.width='100vw';target.style.height='100vh';}"); // resize to max
  1892. Dbl css_w, css_h; emscripten_get_element_css_size(null, &css_w, &css_h); // get actual size
  1893. w=Max(1, RoundPos(css_w*zoom)); h=Max(1, RoundPos(css_h*zoom)); // calculate pixels
  1894. //LogN(S+css_w+' '+css_h+' '+css_w*zoom+' '+css_h*zoom+' '+w+' '+h);
  1895. #endif
  1896. }
  1897. }
  1898. Vec2 css_size=VecI2(w, h)/zoom; // calculate css
  1899. emscripten_set_element_css_size (null, css_size.x, css_size.y); // this accepts floating point sizes
  1900. emscripten_set_canvas_element_size(null, w, h);
  1901. #endif
  1902. }
  1903. void Application::windowDel()
  1904. {
  1905. #if WINDOWS_OLD
  1906. if(Kb._imc){ImmReleaseContext(Hwnd(), Kb._imc); Kb._imc=null;}
  1907. if(PowerNotify)
  1908. {
  1909. if(HMODULE user=GetModuleHandle(L"User32.dll"))
  1910. if(BOOL (WINAPI *UnregisterSuspendResumeNotification)(HPOWERNOTIFY Handle)=(decltype(UnregisterSuspendResumeNotification))GetProcAddress(user, "UnregisterSuspendResumeNotification")) // available on Win8+
  1911. UnregisterSuspendResumeNotification(PowerNotify);
  1912. PowerNotify=null;
  1913. }
  1914. if(_hwnd){DestroyWindow(Hwnd()); _hwnd=null;}
  1915. if(_icon){DestroyIcon (_icon ); _icon=null;}
  1916. UnregisterClass((LPCWSTR)WindowClass, _hinstance);
  1917. #elif WINDOWS_NEW
  1918. #elif MAC
  1919. [OpenGLView release]; OpenGLView=null;
  1920. [ Hwnd() release]; _hwnd =null;
  1921. #elif LINUX
  1922. if( IC ){XDestroyIC (IC ); IC =null;}
  1923. if( IM ){XCloseIM (IM ); IM =null;}
  1924. if(_hwnd ){XDestroyWindow(XDisplay, Hwnd ()); _hwnd =null;}
  1925. if( HwndColormap){XFreeColormap (XDisplay, HwndColormap); HwndColormap=NULL;}
  1926. #else
  1927. WindowClose(hwnd());
  1928. #endif
  1929. _hwnd=null;
  1930. }
  1931. NOINLINE void Application::windowMsg() // disable inline so we will don't use its stack memory, so we can have more memory for application
  1932. {
  1933. #if LINUX
  1934. if(!XDisplay)return;
  1935. #endif
  1936. #if MAC
  1937. NSDate *untilDate=null;
  1938. #else
  1939. Bool wait_end_set=false; UInt wait_end;
  1940. #endif
  1941. if(active_wait && active()) // check 'active_wait' first, because it's most likely zero (rare case)
  1942. {
  1943. if(active_wait<0) // unlimited wait
  1944. {
  1945. #if WINDOWS_OLD
  1946. WaitMessage();
  1947. #elif WINDOWS_NEW
  1948. ExecuteRecordedEvents(); // since we're going to process system loop, first we need to execute any previously recorded events
  1949. Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending);
  1950. #elif MAC
  1951. [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:NO]; // 'distantFuture' will wait for an event
  1952. #elif LINUX
  1953. goto process; // jump to 'XNextEvent' which will block until event occurs
  1954. #endif
  1955. }else // finite wait
  1956. {
  1957. #if WINDOWS_OLD
  1958. MsgWaitForMultipleObjects(0, null, false, active_wait, QS_ALLINPUT);
  1959. #elif WINDOWS_NEW
  1960. // TODO: have to use global "Bool EventOccured" in callbacks, combined with Time.wait(1) ? but that would slow down
  1961. #elif MAC
  1962. [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate dateWithTimeIntervalSinceNow:(active_wait/1000.0)] inMode:NSDefaultRunLoopMode dequeue:NO];
  1963. #elif LINUX
  1964. if(!XEventsQueued(XDisplay, QueuedAfterFlush)) // no events
  1965. {
  1966. wait_end=Time.curTimeMs()+active_wait; // calc end-time
  1967. do Time.wait(1);while(!XEventsQueued(XDisplay, QueuedAfterFlush) // no events
  1968. && Signed(wait_end-Time.curTimeMs())>0);
  1969. }
  1970. #endif
  1971. }
  1972. }
  1973. start:
  1974. #if WINDOWS_OLD
  1975. for(MSG msg; PeekMessage(&msg, null, 0, 0, PM_REMOVE); )
  1976. {
  1977. TranslateMessage(&msg);
  1978. DispatchMessage(&msg);
  1979. }
  1980. if(Activate>=0)
  1981. {
  1982. if(!(NonClientClick && (Ms._clip_rect_on || Ms._clip_window) && Activate)) // if just clicked on the title bar (including Window Min/Max/Close buttons) and the mouse is going to (disappear or get clipped) and we were going to activate, then do nothing, this so that the user can Minimize/Maximize/Close, because otherwise, the mouse cursor would disappear or got clipped and the action wouldn't be performed
  1983. setActive(Activate!=0);
  1984. NonClientClick=false;
  1985. Activate=-1;
  1986. }
  1987. #elif WINDOWS_NEW
  1988. ExecuteRecordedEvents(); // since we're going to process system loop, first we need to execute any previously recorded events
  1989. Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
  1990. again:
  1991. #elif MAC
  1992. for(; NSEvent *event=[NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; ) // 'distantPast' will not wait for any new events but return those that happened already
  1993. [NSApp sendEvent:event];
  1994. #elif LINUX
  1995. for(; XEventsQueued(XDisplay, QueuedAfterFlush); )
  1996. {
  1997. process:
  1998. XEvent event; XNextEvent(XDisplay, &event);
  1999. if(event.xany.window==Hwnd())switch(event.type)
  2000. {
  2001. case MapNotify:
  2002. {
  2003. if(_NET_FRAME_EXTENTS)
  2004. {
  2005. Atom type =NULL;
  2006. int format=0;
  2007. unsigned long items =0, bytes_after=0;
  2008. unsigned char *data =null;
  2009. if(!XGetWindowProperty(XDisplay, Hwnd(), _NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &items, &bytes_after, &data))
  2010. if(type==XA_CARDINAL && format==32 && items>=4)if(long *l=(long*)data)
  2011. {
  2012. long left =l[0],
  2013. right =l[1],
  2014. top =l[2],
  2015. bottom=l[3];
  2016. _bound.set(-left, -top, right, bottom);
  2017. }
  2018. if(data)XFree(data);
  2019. }
  2020. }break;
  2021. case ConfigureNotify:
  2022. {
  2023. _window_resized.set(event.xconfigure.width, event.xconfigure.height);
  2024. _maximized =WindowMaximized();
  2025. // minimized is not available here
  2026. }break;
  2027. case FocusOut: Deactivate=2; break; // when pressing Alt Key, FocusOut/FocusIn may get called multiple times within 0-1 frames, to avoid this, we record the event and deactivate after a delay, during which the app may get 'FocusIn' which will cancel this
  2028. case FocusIn : Deactivate=0; setActive(true); XSetICFocus(IC); break; // disable 'Deactivate'
  2029. case EnterNotify: Ms._on_client=true ; Ms.resetVisibility(); break;
  2030. case LeaveNotify: Ms._on_client=false; Ms.resetVisibility(); break;
  2031. case KeyPress:
  2032. {
  2033. KeyCode code=event.xkey.keycode;
  2034. if(InRange(code, ScanCodeToKey))
  2035. {
  2036. KB_KEY key=ScanCodeToKey[code];
  2037. Kb.push(key, code);
  2038. // !! queue characters after push !!
  2039. if(Kb.anyCtrl()) // if control is pressed then 'chr' is not a character but below 32
  2040. {
  2041. if(key>='A' && key<='Z')Kb.queue(Char(key + (Kb.anyShift() ? 0 : 'a'-'A')), code);else
  2042. if(key>='0' && key<='9')Kb.queue(Char(key ), code);
  2043. }
  2044. }
  2045. // !! queue characters after push !!
  2046. if(IC)
  2047. {
  2048. wchar_t chr[256];
  2049. KeySym symbol=0;
  2050. int status;
  2051. int pressed=XwcLookupString(IC, &event.xkey, chr, Elms(chr), &symbol, &status);
  2052. //code =XKeysymToKeycode(XDisplay, symbol);
  2053. FREP(pressed){Char c=chr[i]; if(c!=127)Kb.queue(c, code);}
  2054. }else
  2055. {
  2056. char chr[256];
  2057. KeySym symbol=0;
  2058. XComposeStatus status;
  2059. int pressed=XLookupString(&event.xkey, chr, Elms(chr), &symbol, &status);
  2060. //code =XKeysymToKeycode(XDisplay, symbol);
  2061. FREP(pressed){Char c=chr[i]; if(c!=127)Kb.queue(c, code);}
  2062. }
  2063. }break;
  2064. case KeyRelease:
  2065. {
  2066. char chr;
  2067. KeySym symbol=0;
  2068. XComposeStatus status;
  2069. XLookupString(&event.xkey, &chr, 1, &symbol, &status);
  2070. KeyCode code=event.xkey.keycode; // XKeysymToKeycode(XDisplay, symbol);
  2071. if(InRange(code, ScanCodeToKey))
  2072. {
  2073. char keymap[32]; XQueryKeymap(XDisplay, keymap); // on Linux 'KeyRelease' may get called multiple times in case the key is still held on, so actual button state (keymap) must be checked
  2074. if(!(keymap[code>>3]&(1<<(code&7))))Kb.release(ScanCodeToKey[code]); // if actually released
  2075. }
  2076. }break;
  2077. case ButtonPress:
  2078. {
  2079. switch(event.xbutton.button)
  2080. {
  2081. case 1: Ms. push (0); break; // LMB
  2082. case 2: Ms. push (2); break; // MMB
  2083. case 3: Ms. push (1); break; // RMB
  2084. case 4: Ms._wheel.y++; break;
  2085. case 5: Ms._wheel.y--; break;
  2086. case 6: Ms._wheel.x--; break;
  2087. case 7: Ms._wheel.x++; break;
  2088. }
  2089. }break;
  2090. case ButtonRelease:
  2091. {
  2092. switch(event.xbutton.button)
  2093. {
  2094. case 1: Ms.release(0); break; // LMB
  2095. case 2: Ms.release(2); break; // MMB
  2096. case 3: Ms.release(1); break; // RMB
  2097. }
  2098. }break;
  2099. case ClientMessage:
  2100. {
  2101. if(event.xclient.message_type==WM_PROTOCOLS)
  2102. {
  2103. if(event.xclient.data.l[0]==WM_DELETE_WINDOW)
  2104. {
  2105. if(!(flag&APP_NO_CLOSE))close();
  2106. }
  2107. }else
  2108. if(event.xclient.message_type==XdndEnter)
  2109. {
  2110. bool use_list=(event.xclient.data.l[1]&1);
  2111. xdnd_source = event.xclient.data.l[0];
  2112. xdnd_version =(event.xclient.data.l[1]>>24);
  2113. if(use_list)
  2114. {
  2115. XProp p; ReadProperty(p, xdnd_source, XdndTypeList);
  2116. xdnd_req=PickTarget((Atom*)p.data, p.count);
  2117. XFree(p.data);
  2118. }else
  2119. {
  2120. xdnd_req=PickTargetFromAtoms(event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
  2121. }
  2122. }else
  2123. if(event.xclient.message_type==XdndPosition)
  2124. {
  2125. XClientMessageEvent m; Zero(m);
  2126. m.type =ClientMessage;
  2127. m.display=event.xclient.display;
  2128. m.window =event.xclient.data.l[0];
  2129. m.message_type=XdndStatus;
  2130. m.format =32;
  2131. m.data.l[0]=Hwnd();
  2132. m.data.l[1]=(xdnd_req!=NULL);
  2133. m.data.l[2]=0;
  2134. m.data.l[3]=0;
  2135. m.data.l[4]=XdndActionCopy;
  2136. XSendEvent(XDisplay, event.xclient.data.l[0], false, NoEventMask, (XEvent*)&m);
  2137. XFlush(XDisplay);
  2138. XWindow child=NULL;
  2139. int x=(event.xclient.data.l[2]>>16)&0xFFFF,
  2140. y= event.xclient.data.l[2] &0xFFFF;
  2141. XTranslateCoordinates(XDisplay, DefaultRootWindow(XDisplay), Hwnd(), x, y, &xdnd_pos.x, &xdnd_pos.y, &child);
  2142. }else
  2143. if(event.xclient.message_type==XdndDrop)
  2144. {
  2145. if(!xdnd_req)
  2146. {
  2147. XClientMessageEvent m; Zero(m);
  2148. m.type =ClientMessage;
  2149. m.display =event.xclient.display;
  2150. m.window =event.xclient.data.l[0];
  2151. m.message_type=XdndFinished;
  2152. m.format =32;
  2153. m.data.l[0] =Hwnd();
  2154. m.data.l[1] =0;
  2155. m.data.l[2] =0;
  2156. XSendEvent(XDisplay, event.xclient.data.l[0], false, NoEventMask, (XEvent*)&m);
  2157. }else
  2158. {
  2159. if(xdnd_version>=1)
  2160. {
  2161. XConvertSelection(XDisplay, XdndSelection, xdnd_req, PRIMARY, Hwnd(), event.xclient.data.l[2]);
  2162. }else
  2163. {
  2164. XConvertSelection(XDisplay, XdndSelection, xdnd_req, PRIMARY, Hwnd(), CurrentTime);
  2165. }
  2166. }
  2167. }
  2168. }break;
  2169. case SelectionNotify:
  2170. {
  2171. if(event.xselection.target==xdnd_req)
  2172. {
  2173. Memc<Str> names;
  2174. XProp p; ReadProperty(p, Hwnd(), PRIMARY);
  2175. if(p.format==8)
  2176. {
  2177. Str8 s=Replace((CChar8*)p.data, "file://", S), o;
  2178. o.reserve(s.length());
  2179. FREPA(s)
  2180. {
  2181. Char8 c=s[i];
  2182. if(c!='\r')
  2183. {
  2184. if(c=='%')
  2185. {
  2186. o+=Char8((CharInt(s[i+1])<<4)|CharInt(s[i+2]));
  2187. i+=2;
  2188. }else o+=c;
  2189. }
  2190. }
  2191. Split(names, FromUTF8(o), '\n');
  2192. if(names.elms() && !names.last().is())names.removeLast();
  2193. }
  2194. XFree(p.data);
  2195. // send reply
  2196. XClientMessageEvent m; Zero(m);
  2197. m.type =ClientMessage;
  2198. m.display =XDisplay;
  2199. m.window =xdnd_source;
  2200. m.message_type=XdndFinished;
  2201. m.format =32;
  2202. m.data.l[0]=Hwnd();
  2203. m.data.l[1]=1;
  2204. m.data.l[2]=XdndActionCopy;
  2205. XSendEvent(XDisplay, xdnd_source, false, NoEventMask, (XEvent*)&m);
  2206. XSync(XDisplay, false);
  2207. if(drop)
  2208. {
  2209. Vec2 pos=D.windowPixelToScreen(xdnd_pos);
  2210. drop(names, Gui.objAtPos(pos), pos);
  2211. }
  2212. }
  2213. }break;
  2214. case SelectionRequest:
  2215. {
  2216. int format=0;
  2217. unsigned long items=0;
  2218. unsigned long overflow=0;
  2219. unsigned char *data=null;
  2220. XSelectionRequestEvent &req=event.xselectionrequest;
  2221. XEvent sevent; Zero(sevent);
  2222. sevent.xany.type =SelectionNotify;
  2223. sevent.xselection.selection=req.selection;
  2224. sevent.xselection.target =NULL;
  2225. sevent.xselection.property =NULL;
  2226. sevent.xselection.requestor=req.requestor;
  2227. sevent.xselection.time =req.time;
  2228. if(!XGetWindowProperty(XDisplay, DefaultRootWindow(XDisplay), XA_CUT_BUFFER0, 0, INT_MAX/4, false, req.target, &sevent.xselection.target, &format, &items, &overflow, &data))
  2229. {
  2230. Atom XA_TARGETS=XInternAtom(XDisplay, "TARGETS", 0);
  2231. if(sevent.xselection.target==req.target)
  2232. {
  2233. XChangeProperty(XDisplay, req.requestor, req.property, sevent.xselection.target, format, PropModeReplace, data, items);
  2234. sevent.xselection.property=req.property;
  2235. }else
  2236. if(XA_TARGETS==req.target)
  2237. {
  2238. Atom SupportedFormats[]={sevent.xselection.target, XA_TARGETS};
  2239. XChangeProperty(XDisplay, req.requestor, req.property, XA_ATOM, 32, PropModeReplace, (unsigned char*)SupportedFormats, Elms(SupportedFormats));
  2240. sevent.xselection.property=req.property;
  2241. }
  2242. }
  2243. if(data)XFree(data);
  2244. XSendEvent(XDisplay, req.requestor, false, 0, &sevent);
  2245. XSync(XDisplay, false);
  2246. }break;
  2247. }
  2248. switch(event.type)
  2249. {
  2250. /*case KeymapNotify: if(active()) ignore this because of the issue 1) Mouse-click on App to activate (must be by mouse) 2) Alt+Tab to lose focus 3) Alt+Tab to restore focus 4) Alt gets stuck, and worse if Alt+F4 some other app then Alt+F4 gets copied to this app as well, if wanted to be restored then 'KeymapStateMask' have to be re-enabled
  2251. {
  2252. REPA(event.xkeymap.key_vector)if(Byte b=(Byte)event.xkeymap.key_vector[i])
  2253. REPD(bit, 8)if(b&(1<<bit))
  2254. {
  2255. Int index=(bit+(i<<3));
  2256. if(InRange(index, ScanCodeToKey))Kb.push(ScanCodeToKey[index]);
  2257. }
  2258. }break;*/
  2259. case MappingNotify:
  2260. {
  2261. XMappingEvent &map=event.xmapping;
  2262. if(map.request==MappingKeyboard)Kb.setLayout();
  2263. }break;
  2264. case GenericEvent: if(event.xcookie.extension==XInput2Extension)
  2265. {
  2266. if(XGetEventData(XDisplay, &event.xcookie))
  2267. {
  2268. switch(event.xcookie.evtype)
  2269. {
  2270. case XI_RawMotion:
  2271. {
  2272. XIRawEvent &raw=*(XIRawEvent*)event.xcookie.data;
  2273. Dbl delta[2]; SetXInputValues(raw.raw_values, raw.valuators.mask, raw.valuators.mask_len, delta, Elms(delta));
  2274. Ms._delta_relative.x+=delta[0];
  2275. Ms._delta_relative.y-=delta[1];
  2276. }break;
  2277. case XI_RawButtonPress: if(active()) // this will get called even if app is not active
  2278. {
  2279. XIRawEvent &raw=*(XIRawEvent*)event.xcookie.data;
  2280. Int b=raw.detail-5; // 8 is button #3, 9 is button #4, .., other buttons are handled in 'ButtonPress'
  2281. if( b>=3 && InRange(b, ButtonPressCount)) // on Linux there's some issue that extra buttons get pushed,pushed,released in the same frame
  2282. {
  2283. if(!ButtonPressCount[b]++)Ms.push(b); // push only at first
  2284. }
  2285. }break;
  2286. case XI_RawButtonRelease:
  2287. {
  2288. XIRawEvent &raw=*(XIRawEvent*)event.xcookie.data;
  2289. Int b=raw.detail-5; // 8 is button #3, 9 is button #4, .., other buttons are handled in 'ButtonRelease'
  2290. if( b>=3 && InRange(b, ButtonPressCount) && ButtonPressCount[b])
  2291. {
  2292. if(!--ButtonPressCount[b])Ms.release(b); // release only at last
  2293. }
  2294. }break;
  2295. case XI_TouchBegin:
  2296. {
  2297. XIDeviceEvent &dev=*(XIDeviceEvent*)event.xcookie.data;
  2298. //xev->sourceid, xev->detail, xev->event_x, xev->event_y
  2299. }break;
  2300. case XI_TouchEnd:
  2301. {
  2302. XIDeviceEvent &dev=*(XIDeviceEvent*)event.xcookie.data;
  2303. //xev->sourceid, xev->detail, xev->event_x, xev->event_y
  2304. }break;
  2305. case XI_TouchUpdate:
  2306. {
  2307. XIDeviceEvent &dev=*(XIDeviceEvent*)event.xcookie.data;
  2308. //xev->sourceid, xev->detail, xev->event_x, xev->event_y
  2309. }break;
  2310. }
  2311. XFreeEventData(XDisplay, &event.xcookie);
  2312. }
  2313. }break;
  2314. }
  2315. }
  2316. if(Deactivate && !--Deactivate){setActive(false); XUnsetICFocus(IC);}
  2317. #endif
  2318. if(!active() && !_close)
  2319. if(Int wait=((flag&APP_WORK_IN_BACKGROUND) ? background_wait : -1))
  2320. {
  2321. if(wait<0) // unlimited wait
  2322. {
  2323. #if WINDOWS_OLD
  2324. WaitMessage(); goto start;
  2325. #elif WINDOWS_NEW
  2326. Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending);
  2327. goto again;
  2328. #elif MAC
  2329. if([NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:NO])goto start; // 'distantFuture' will wait for an event
  2330. _close=true; // if no event got received in unlimited wait, then perhaps app wants to be closed, request the main thread to close the app (but don't call 'App.close' because that would call quit)
  2331. #elif LINUX
  2332. goto process; // jump to 'XNextEvent' which will block until event occurs
  2333. #endif
  2334. }else // finite wait
  2335. {
  2336. #if MAC
  2337. if(!untilDate)untilDate=[NSDate dateWithTimeIntervalSinceNow:(wait/1000.0)];
  2338. if([NSApp nextEventMatchingMask:NSEventMaskAny untilDate:untilDate inMode:NSDefaultRunLoopMode dequeue:NO])goto start;
  2339. #else
  2340. if(wait_end_set) // if we already have the end time limit
  2341. {
  2342. wait=wait_end-Time.curTimeMs(); if(wait<=0)goto stop; // calculate remaining time
  2343. }else
  2344. {
  2345. wait_end=Time.curTimeMs()+wait; wait_end_set=true;
  2346. }
  2347. #if WINDOWS_OLD
  2348. if(MsgWaitForMultipleObjects(0, null, false, wait, QS_ALLINPUT)!=WAIT_TIMEOUT)goto start;
  2349. #else
  2350. Time.wait(1); goto start;
  2351. #endif
  2352. #endif
  2353. }
  2354. }
  2355. stop:
  2356. if(_window_resized.x>0 && _window_resized.y>0)
  2357. {
  2358. D.modeSet(_window_resized.x, _window_resized.y);
  2359. _window_resized=-1;
  2360. }
  2361. #if MAC
  2362. PreventResizing=false;
  2363. #endif
  2364. }
  2365. #if WINDOWS || MAC || LINUX
  2366. void Application::loop()
  2367. {
  2368. for(;;)
  2369. {
  2370. windowMsg();
  2371. if(_close)break;
  2372. #if WINDOWS_OLD
  2373. Updating=true; // set as updating to prevent WM_TIMER-based updates occurring inside this scope
  2374. #endif
  2375. update();
  2376. #if WINDOWS_OLD
  2377. Updating=false;
  2378. #endif
  2379. }
  2380. }
  2381. #endif
  2382. /******************************************************************************/
  2383. void InitWindow() // this is called again inside 'App.coInitialize' !!
  2384. {
  2385. #if WINDOWS_OLD
  2386. CoCreateInstance(CLSID_TaskbarList, null, CLSCTX_ALL, IID_ITaskbarList3, (Ptr*)&TaskbarList);
  2387. SetLastError(0); // clear error 2
  2388. #elif LINUX
  2389. if(XDisplay)
  2390. {
  2391. FIND_ATOM( WM_STATE);
  2392. FIND_ATOM(_NET_WM_STATE);
  2393. FIND_ATOM(_NET_WM_STATE_HIDDEN);
  2394. FIND_ATOM(_NET_WM_STATE_FOCUSED);
  2395. FIND_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
  2396. FIND_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
  2397. FIND_ATOM(_NET_WM_STATE_FULLSCREEN);
  2398. FIND_ATOM(_NET_WM_STATE_DEMANDS_ATTENTION);
  2399. FIND_ATOM(_NET_FRAME_EXTENTS);
  2400. FIND_ATOM(_NET_WM_NAME);
  2401. FIND_ATOM(UTF8_STRING);
  2402. FIND_ATOM(_MOTIF_WM_HINTS);
  2403. }
  2404. #endif
  2405. }
  2406. void ShutWindow() // this is called again inside 'App.coInitialize' !!
  2407. {
  2408. #if WINDOWS_OLD
  2409. RELEASE(TaskbarList);
  2410. #endif
  2411. }
  2412. /******************************************************************************/
  2413. }
  2414. /******************************************************************************/