PageRenderTime 67ms CodeModel.GetById 42ms RepoModel.GetById 1ms app.codeStats 0ms

/dlls/winemac.drv/dllmain.c

https://github.com/mirrors/wine
C | 440 lines | 327 code | 74 blank | 39 comment | 54 complexity | 1a868f4d3aaa50bc075e518e1bfcb6ae MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, LGPL-2.0, CC-BY-SA-3.0, BSD-3-Clause
  1. /*
  2. * winemac.drv entry points
  3. *
  4. * Copyright 2022 Jacek Caban for CodeWeavers
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19. */
  20. #include "macdrv_dll.h"
  21. #include "macdrv_res.h"
  22. #include "shellapi.h"
  23. #include "wine/debug.h"
  24. WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
  25. HMODULE macdrv_module = 0;
  26. static unixlib_handle_t macdrv_handle;
  27. NTSTATUS (CDECL *macdrv_unix_call)(enum macdrv_funcs code, void *params);
  28. struct quit_info {
  29. HWND *wins;
  30. UINT capacity;
  31. UINT count;
  32. UINT done;
  33. DWORD flags;
  34. BOOL result;
  35. BOOL replied;
  36. };
  37. static BOOL CALLBACK get_process_windows(HWND hwnd, LPARAM lp)
  38. {
  39. struct quit_info *qi = (struct quit_info*)lp;
  40. DWORD pid;
  41. NtUserGetWindowThread(hwnd, &pid);
  42. if (pid == GetCurrentProcessId())
  43. {
  44. if (qi->count >= qi->capacity)
  45. {
  46. UINT new_cap = qi->capacity * 2;
  47. HWND *new_wins = HeapReAlloc(GetProcessHeap(), 0, qi->wins, new_cap * sizeof(*qi->wins));
  48. if (!new_wins) return FALSE;
  49. qi->wins = new_wins;
  50. qi->capacity = new_cap;
  51. }
  52. qi->wins[qi->count++] = hwnd;
  53. }
  54. return TRUE;
  55. }
  56. #include "pshpack1.h"
  57. typedef struct
  58. {
  59. BYTE bWidth;
  60. BYTE bHeight;
  61. BYTE bColorCount;
  62. BYTE bReserved;
  63. WORD wPlanes;
  64. WORD wBitCount;
  65. DWORD dwBytesInRes;
  66. WORD nID;
  67. } GRPICONDIRENTRY;
  68. typedef struct
  69. {
  70. WORD idReserved;
  71. WORD idType;
  72. WORD idCount;
  73. GRPICONDIRENTRY idEntries[1];
  74. } GRPICONDIR;
  75. #include "poppack.h"
  76. static void quit_reply(int reply)
  77. {
  78. struct quit_result_params params = { .result = reply };
  79. MACDRV_CALL(quit_result, &params);
  80. }
  81. static void CALLBACK quit_callback(HWND hwnd, UINT msg, ULONG_PTR data, LRESULT result)
  82. {
  83. struct quit_info *qi = (struct quit_info*)data;
  84. qi->done++;
  85. if (msg == WM_QUERYENDSESSION)
  86. {
  87. TRACE("got WM_QUERYENDSESSION result %ld from win %p (%u of %u done)\n", result,
  88. hwnd, qi->done, qi->count);
  89. if (!result && !IsWindow(hwnd))
  90. {
  91. TRACE("win %p no longer exists; ignoring apparent refusal\n", hwnd);
  92. result = TRUE;
  93. }
  94. if (!result && qi->result)
  95. {
  96. qi->result = FALSE;
  97. /* On the first FALSE from WM_QUERYENDSESSION, we already know the
  98. ultimate reply. Might as well tell Cocoa now. */
  99. if (!qi->replied)
  100. {
  101. qi->replied = TRUE;
  102. TRACE("giving quit reply %d\n", qi->result);
  103. quit_reply(qi->result);
  104. }
  105. }
  106. if (qi->done >= qi->count)
  107. {
  108. UINT i;
  109. qi->done = 0;
  110. for (i = 0; i < qi->count; i++)
  111. {
  112. TRACE("sending WM_ENDSESSION to win %p result %d flags 0x%08x\n", qi->wins[i],
  113. qi->result, qi->flags);
  114. if (!SendMessageCallbackW(qi->wins[i], WM_ENDSESSION, qi->result, qi->flags,
  115. quit_callback, (ULONG_PTR)qi))
  116. {
  117. WARN("failed to send WM_ENDSESSION to win %p; error 0x%08x\n",
  118. qi->wins[i], GetLastError());
  119. quit_callback(qi->wins[i], WM_ENDSESSION, (ULONG_PTR)qi, 0);
  120. }
  121. }
  122. }
  123. }
  124. else /* WM_ENDSESSION */
  125. {
  126. TRACE("finished WM_ENDSESSION for win %p (%u of %u done)\n", hwnd, qi->done, qi->count);
  127. if (qi->done >= qi->count)
  128. {
  129. if (!qi->replied)
  130. {
  131. TRACE("giving quit reply %d\n", qi->result);
  132. quit_reply(qi->result);
  133. }
  134. TRACE("%sterminating process\n", qi->result ? "" : "not ");
  135. if (qi->result)
  136. TerminateProcess(GetCurrentProcess(), 0);
  137. HeapFree(GetProcessHeap(), 0, qi->wins);
  138. HeapFree(GetProcessHeap(), 0, qi);
  139. }
  140. }
  141. }
  142. /***********************************************************************
  143. * macdrv_app_quit_request
  144. */
  145. NTSTATUS WINAPI macdrv_app_quit_request(void *arg, ULONG size)
  146. {
  147. struct app_quit_request_params *params = arg;
  148. struct quit_info *qi;
  149. UINT i;
  150. qi = HeapAlloc(GetProcessHeap(), 0, sizeof(*qi));
  151. if (!qi)
  152. goto fail;
  153. qi->capacity = 32;
  154. qi->wins = HeapAlloc(GetProcessHeap(), 0, qi->capacity * sizeof(*qi->wins));
  155. qi->count = qi->done = 0;
  156. if (!qi->wins || !EnumWindows(get_process_windows, (LPARAM)qi))
  157. goto fail;
  158. qi->flags = params->flags;
  159. qi->result = TRUE;
  160. qi->replied = FALSE;
  161. for (i = 0; i < qi->count; i++)
  162. {
  163. TRACE("sending WM_QUERYENDSESSION to win %p\n", qi->wins[i]);
  164. if (!SendMessageCallbackW(qi->wins[i], WM_QUERYENDSESSION, 0, qi->flags,
  165. quit_callback, (ULONG_PTR)qi))
  166. {
  167. DWORD error = GetLastError();
  168. BOOL invalid = (error == ERROR_INVALID_WINDOW_HANDLE);
  169. if (invalid)
  170. TRACE("failed to send WM_QUERYENDSESSION to win %p because it's invalid; assuming success\n",
  171. qi->wins[i]);
  172. else
  173. WARN("failed to send WM_QUERYENDSESSION to win %p; error 0x%08x; assuming refusal\n",
  174. qi->wins[i], error);
  175. quit_callback(qi->wins[i], WM_QUERYENDSESSION, (ULONG_PTR)qi, invalid);
  176. }
  177. }
  178. /* quit_callback() will clean up qi */
  179. return 0;
  180. fail:
  181. WARN("failed to allocate window list\n");
  182. if (qi)
  183. {
  184. HeapFree(GetProcessHeap(), 0, qi->wins);
  185. HeapFree(GetProcessHeap(), 0, qi);
  186. }
  187. quit_reply(FALSE);
  188. return 0;
  189. }
  190. /***********************************************************************
  191. * get_first_resource
  192. *
  193. * Helper for create_app_icon_images(). Enum proc for EnumResourceNamesW()
  194. * which just gets the handle for the first resource and stops further
  195. * enumeration.
  196. */
  197. static BOOL CALLBACK get_first_resource(HMODULE module, LPCWSTR type, LPWSTR name, LONG_PTR lparam)
  198. {
  199. HRSRC *res_info = (HRSRC*)lparam;
  200. *res_info = FindResourceW(module, name, (LPCWSTR)RT_GROUP_ICON);
  201. return FALSE;
  202. }
  203. /***********************************************************************
  204. * macdrv_app_icon
  205. */
  206. static NTSTATUS WINAPI macdrv_app_icon(void *arg, ULONG size)
  207. {
  208. struct app_icon_params *params = arg;
  209. struct app_icon_result *result = params->result;
  210. HRSRC res_info;
  211. HGLOBAL res_data;
  212. GRPICONDIR *icon_dir;
  213. int i;
  214. TRACE("()\n");
  215. result->count = 0;
  216. res_info = NULL;
  217. EnumResourceNamesW(NULL, (LPCWSTR)RT_GROUP_ICON, get_first_resource, (LONG_PTR)&res_info);
  218. if (!res_info)
  219. {
  220. WARN("found no RT_GROUP_ICON resource\n");
  221. return 0;
  222. }
  223. if (!(res_data = LoadResource(NULL, res_info)))
  224. {
  225. WARN("failed to load RT_GROUP_ICON resource\n");
  226. return 0;
  227. }
  228. if (!(icon_dir = LockResource(res_data)))
  229. {
  230. WARN("failed to lock RT_GROUP_ICON resource\n");
  231. goto cleanup;
  232. }
  233. for (i = 0; i < icon_dir->idCount && result->count < ARRAYSIZE(result->entries); i++)
  234. {
  235. struct app_icon_entry *entry = &result->entries[result->count];
  236. int width = icon_dir->idEntries[i].bWidth;
  237. int height = icon_dir->idEntries[i].bHeight;
  238. BOOL found_better_bpp = FALSE;
  239. int j;
  240. LPCWSTR name;
  241. HGLOBAL icon_res_data;
  242. BYTE *icon_bits;
  243. if (!width) width = 256;
  244. if (!height) height = 256;
  245. /* If there's another icon at the same size but with better
  246. color depth, skip this one. We end up making CGImages that
  247. are all 32 bits per pixel, so Cocoa doesn't get the original
  248. color depth info to pick the best representation itself. */
  249. for (j = 0; j < icon_dir->idCount; j++)
  250. {
  251. int jwidth = icon_dir->idEntries[j].bWidth;
  252. int jheight = icon_dir->idEntries[j].bHeight;
  253. if (!jwidth) jwidth = 256;
  254. if (!jheight) jheight = 256;
  255. if (j != i && jwidth == width && jheight == height &&
  256. icon_dir->idEntries[j].wBitCount > icon_dir->idEntries[i].wBitCount)
  257. {
  258. found_better_bpp = TRUE;
  259. break;
  260. }
  261. }
  262. if (found_better_bpp) continue;
  263. name = MAKEINTRESOURCEW(icon_dir->idEntries[i].nID);
  264. res_info = FindResourceW(NULL, name, (LPCWSTR)RT_ICON);
  265. if (!res_info)
  266. {
  267. WARN("failed to find RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
  268. continue;
  269. }
  270. icon_res_data = LoadResource(NULL, res_info);
  271. if (!icon_res_data)
  272. {
  273. WARN("failed to load icon %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
  274. continue;
  275. }
  276. icon_bits = LockResource(icon_res_data);
  277. if (icon_bits)
  278. {
  279. static const BYTE png_magic[] = { 0x89, 0x50, 0x4e, 0x47 };
  280. entry->width = width;
  281. entry->height = height;
  282. entry->size = icon_dir->idEntries[i].dwBytesInRes;
  283. if (!memcmp(icon_bits, png_magic, sizeof(png_magic)))
  284. {
  285. entry->png = icon_bits;
  286. entry->icon = 0;
  287. result->count++;
  288. }
  289. else
  290. {
  291. entry->icon = CreateIconFromResourceEx(icon_bits, icon_dir->idEntries[i].dwBytesInRes,
  292. TRUE, 0x00030000, width, height, 0);
  293. if (entry->icon)
  294. {
  295. entry->png = NULL;
  296. result->count++;
  297. }
  298. else
  299. WARN("failed to create icon %d from resource with ID %hd\n", i, icon_dir->idEntries[i].nID);
  300. }
  301. }
  302. else
  303. WARN("failed to lock RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
  304. FreeResource(icon_res_data);
  305. }
  306. cleanup:
  307. FreeResource(res_data);
  308. return 0;
  309. }
  310. typedef NTSTATUS (WINAPI *kernel_callback)(void *params, ULONG size);
  311. static const kernel_callback kernel_callbacks[] =
  312. {
  313. macdrv_app_icon,
  314. macdrv_app_quit_request,
  315. macdrv_dnd_query_drag,
  316. macdrv_dnd_query_drop,
  317. macdrv_dnd_query_exited,
  318. macdrv_ime_query_char_rect,
  319. macdrv_ime_set_text,
  320. };
  321. C_ASSERT(NtUserDriverCallbackFirst + ARRAYSIZE(kernel_callbacks) == client_func_last);
  322. static BOOL process_attach(void)
  323. {
  324. struct init_params params;
  325. void **callback_table;
  326. struct localized_string *str;
  327. struct localized_string strings[] = {
  328. { .id = STRING_MENU_WINE },
  329. { .id = STRING_MENU_ITEM_HIDE_APPNAME },
  330. { .id = STRING_MENU_ITEM_HIDE },
  331. { .id = STRING_MENU_ITEM_HIDE_OTHERS },
  332. { .id = STRING_MENU_ITEM_SHOW_ALL },
  333. { .id = STRING_MENU_ITEM_QUIT_APPNAME },
  334. { .id = STRING_MENU_ITEM_QUIT },
  335. { .id = STRING_MENU_WINDOW },
  336. { .id = STRING_MENU_ITEM_MINIMIZE },
  337. { .id = STRING_MENU_ITEM_ZOOM },
  338. { .id = STRING_MENU_ITEM_ENTER_FULL_SCREEN },
  339. { .id = STRING_MENU_ITEM_BRING_ALL_TO_FRONT },
  340. { .id = 0 }
  341. };
  342. if (NtQueryVirtualMemory(GetCurrentProcess(), macdrv_module, MemoryWineUnixFuncs,
  343. &macdrv_handle, sizeof(macdrv_handle), NULL))
  344. return FALSE;
  345. for (str = strings; str->id; str++)
  346. str->len = LoadStringW(macdrv_module, str->id, (WCHAR *)&str->str, 0);
  347. params.strings = strings;
  348. params.pNtWaitForMultipleObjects = NtWaitForMultipleObjects;
  349. if (__wine_unix_call(macdrv_handle, unix_init, &params)) return FALSE;
  350. callback_table = NtCurrentTeb()->Peb->KernelCallbackTable;
  351. memcpy( callback_table + NtUserDriverCallbackFirst, kernel_callbacks, sizeof(kernel_callbacks) );
  352. macdrv_unix_call = params.unix_call;
  353. return TRUE;
  354. }
  355. BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
  356. {
  357. if (reason != DLL_PROCESS_ATTACH) return TRUE;
  358. DisableThreadLibraryCalls(instance);
  359. macdrv_module = instance;
  360. return process_attach();
  361. }
  362. int CDECL wine_notify_icon(DWORD msg, NOTIFYICONDATAW *data)
  363. {
  364. struct notify_icon_params params = { .msg = msg, .data = data };
  365. return MACDRV_CALL(notify_icon, &params);
  366. }