winemac: Use unixlib interface for macdrv_app_icon.
[wine.git] / dlls / winemac.drv / dllmain.c
blobe281a473e07da3ad7dc2590318799b4f5a18af11
1 /*
2 * winemac.drv entry points
4 * Copyright 2022 Jacek Caban for CodeWeavers
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.
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.
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
21 #include "config.h"
22 #include <stdarg.h>
23 #include "macdrv.h"
24 #include "shellapi.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(macdrv);
30 HMODULE macdrv_module = 0;
32 struct quit_info {
33 HWND *wins;
34 UINT capacity;
35 UINT count;
36 UINT done;
37 DWORD flags;
38 BOOL result;
39 BOOL replied;
43 static BOOL CALLBACK get_process_windows(HWND hwnd, LPARAM lp)
45 struct quit_info *qi = (struct quit_info*)lp;
46 DWORD pid;
48 NtUserGetWindowThread(hwnd, &pid);
49 if (pid == GetCurrentProcessId())
51 if (qi->count >= qi->capacity)
53 UINT new_cap = qi->capacity * 2;
54 HWND *new_wins = HeapReAlloc(GetProcessHeap(), 0, qi->wins, new_cap * sizeof(*qi->wins));
55 if (!new_wins) return FALSE;
56 qi->wins = new_wins;
57 qi->capacity = new_cap;
60 qi->wins[qi->count++] = hwnd;
63 return TRUE;
66 #include "pshpack1.h"
68 typedef struct
70 BYTE bWidth;
71 BYTE bHeight;
72 BYTE bColorCount;
73 BYTE bReserved;
74 WORD wPlanes;
75 WORD wBitCount;
76 DWORD dwBytesInRes;
77 WORD nID;
78 } GRPICONDIRENTRY;
80 typedef struct
82 WORD idReserved;
83 WORD idType;
84 WORD idCount;
85 GRPICONDIRENTRY idEntries[1];
86 } GRPICONDIR;
88 #include "poppack.h"
90 static void quit_reply(int reply)
92 struct quit_result_params params = { .result = reply };
93 MACDRV_CALL(quit_result, &params);
97 static void CALLBACK quit_callback(HWND hwnd, UINT msg, ULONG_PTR data, LRESULT result)
99 struct quit_info *qi = (struct quit_info*)data;
101 qi->done++;
103 if (msg == WM_QUERYENDSESSION)
105 TRACE("got WM_QUERYENDSESSION result %ld from win %p (%u of %u done)\n", result,
106 hwnd, qi->done, qi->count);
108 if (!result && !IsWindow(hwnd))
110 TRACE("win %p no longer exists; ignoring apparent refusal\n", hwnd);
111 result = TRUE;
114 if (!result && qi->result)
116 qi->result = FALSE;
118 /* On the first FALSE from WM_QUERYENDSESSION, we already know the
119 ultimate reply. Might as well tell Cocoa now. */
120 if (!qi->replied)
122 qi->replied = TRUE;
123 TRACE("giving quit reply %d\n", qi->result);
124 quit_reply(qi->result);
128 if (qi->done >= qi->count)
130 UINT i;
132 qi->done = 0;
133 for (i = 0; i < qi->count; i++)
135 TRACE("sending WM_ENDSESSION to win %p result %d flags 0x%08x\n", qi->wins[i],
136 qi->result, qi->flags);
137 if (!SendMessageCallbackW(qi->wins[i], WM_ENDSESSION, qi->result, qi->flags,
138 quit_callback, (ULONG_PTR)qi))
140 WARN("failed to send WM_ENDSESSION to win %p; error 0x%08x\n",
141 qi->wins[i], GetLastError());
142 quit_callback(qi->wins[i], WM_ENDSESSION, (ULONG_PTR)qi, 0);
147 else /* WM_ENDSESSION */
149 TRACE("finished WM_ENDSESSION for win %p (%u of %u done)\n", hwnd, qi->done, qi->count);
151 if (qi->done >= qi->count)
153 if (!qi->replied)
155 TRACE("giving quit reply %d\n", qi->result);
156 quit_reply(qi->result);
159 TRACE("%sterminating process\n", qi->result ? "" : "not ");
160 if (qi->result)
161 TerminateProcess(GetCurrentProcess(), 0);
163 HeapFree(GetProcessHeap(), 0, qi->wins);
164 HeapFree(GetProcessHeap(), 0, qi);
170 /***********************************************************************
171 * macdrv_app_quit_request
173 NTSTATUS WINAPI macdrv_app_quit_request(void *arg, ULONG size)
175 struct app_quit_request_params *params = arg;
176 struct quit_info *qi;
177 UINT i;
179 qi = HeapAlloc(GetProcessHeap(), 0, sizeof(*qi));
180 if (!qi)
181 goto fail;
183 qi->capacity = 32;
184 qi->wins = HeapAlloc(GetProcessHeap(), 0, qi->capacity * sizeof(*qi->wins));
185 qi->count = qi->done = 0;
187 if (!qi->wins || !EnumWindows(get_process_windows, (LPARAM)qi))
188 goto fail;
190 qi->flags = params->flags;
191 qi->result = TRUE;
192 qi->replied = FALSE;
194 for (i = 0; i < qi->count; i++)
196 TRACE("sending WM_QUERYENDSESSION to win %p\n", qi->wins[i]);
197 if (!SendMessageCallbackW(qi->wins[i], WM_QUERYENDSESSION, 0, qi->flags,
198 quit_callback, (ULONG_PTR)qi))
200 DWORD error = GetLastError();
201 BOOL invalid = (error == ERROR_INVALID_WINDOW_HANDLE);
202 if (invalid)
203 TRACE("failed to send WM_QUERYENDSESSION to win %p because it's invalid; assuming success\n",
204 qi->wins[i]);
205 else
206 WARN("failed to send WM_QUERYENDSESSION to win %p; error 0x%08x; assuming refusal\n",
207 qi->wins[i], error);
208 quit_callback(qi->wins[i], WM_QUERYENDSESSION, (ULONG_PTR)qi, invalid);
212 /* quit_callback() will clean up qi */
213 return 0;
215 fail:
216 WARN("failed to allocate window list\n");
217 if (qi)
219 HeapFree(GetProcessHeap(), 0, qi->wins);
220 HeapFree(GetProcessHeap(), 0, qi);
222 quit_reply(FALSE);
223 return 0;
226 /***********************************************************************
227 * get_first_resource
229 * Helper for create_app_icon_images(). Enum proc for EnumResourceNamesW()
230 * which just gets the handle for the first resource and stops further
231 * enumeration.
233 static BOOL CALLBACK get_first_resource(HMODULE module, LPCWSTR type, LPWSTR name, LONG_PTR lparam)
235 HRSRC *res_info = (HRSRC*)lparam;
237 *res_info = FindResourceW(module, name, (LPCWSTR)RT_GROUP_ICON);
238 return FALSE;
242 /***********************************************************************
243 * macdrv_app_icon
245 static NTSTATUS WINAPI macdrv_app_icon(void *arg, ULONG size)
247 struct app_icon_params *params = arg;
248 struct app_icon_result *result = params->result;
249 HRSRC res_info;
250 HGLOBAL res_data;
251 GRPICONDIR *icon_dir;
252 int i;
254 TRACE("()\n");
256 result->count = 0;
258 res_info = NULL;
259 EnumResourceNamesW(NULL, (LPCWSTR)RT_GROUP_ICON, get_first_resource, (LONG_PTR)&res_info);
260 if (!res_info)
262 WARN("found no RT_GROUP_ICON resource\n");
263 return 0;
266 if (!(res_data = LoadResource(NULL, res_info)))
268 WARN("failed to load RT_GROUP_ICON resource\n");
269 return 0;
272 if (!(icon_dir = LockResource(res_data)))
274 WARN("failed to lock RT_GROUP_ICON resource\n");
275 goto cleanup;
278 for (i = 0; i < icon_dir->idCount && result->count < ARRAYSIZE(result->entries); i++)
280 struct app_icon_entry *entry = &result->entries[result->count];
281 int width = icon_dir->idEntries[i].bWidth;
282 int height = icon_dir->idEntries[i].bHeight;
283 BOOL found_better_bpp = FALSE;
284 int j;
285 LPCWSTR name;
286 HGLOBAL icon_res_data;
287 BYTE *icon_bits;
289 if (!width) width = 256;
290 if (!height) height = 256;
292 /* If there's another icon at the same size but with better
293 color depth, skip this one. We end up making CGImages that
294 are all 32 bits per pixel, so Cocoa doesn't get the original
295 color depth info to pick the best representation itself. */
296 for (j = 0; j < icon_dir->idCount; j++)
298 int jwidth = icon_dir->idEntries[j].bWidth;
299 int jheight = icon_dir->idEntries[j].bHeight;
301 if (!jwidth) jwidth = 256;
302 if (!jheight) jheight = 256;
304 if (j != i && jwidth == width && jheight == height &&
305 icon_dir->idEntries[j].wBitCount > icon_dir->idEntries[i].wBitCount)
307 found_better_bpp = TRUE;
308 break;
312 if (found_better_bpp) continue;
314 name = MAKEINTRESOURCEW(icon_dir->idEntries[i].nID);
315 res_info = FindResourceW(NULL, name, (LPCWSTR)RT_ICON);
316 if (!res_info)
318 WARN("failed to find RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
319 continue;
322 icon_res_data = LoadResource(NULL, res_info);
323 if (!icon_res_data)
325 WARN("failed to load icon %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
326 continue;
329 icon_bits = LockResource(icon_res_data);
330 if (icon_bits)
332 static const BYTE png_magic[] = { 0x89, 0x50, 0x4e, 0x47 };
334 entry->width = width;
335 entry->height = height;
336 entry->size = icon_dir->idEntries[i].dwBytesInRes;
338 if (!memcmp(icon_bits, png_magic, sizeof(png_magic)))
340 entry->png = icon_bits;
341 entry->icon = 0;
342 result->count++;
344 else
346 entry->icon = CreateIconFromResourceEx(icon_bits, icon_dir->idEntries[i].dwBytesInRes,
347 TRUE, 0x00030000, width, height, 0);
348 if (entry->icon)
350 entry->png = NULL;
351 result->count++;
353 else
354 WARN("failed to create icon %d from resource with ID %hd\n", i, icon_dir->idEntries[i].nID);
357 else
358 WARN("failed to lock RT_ICON resource %d with ID %hd\n", i, icon_dir->idEntries[i].nID);
360 FreeResource(icon_res_data);
363 cleanup:
364 FreeResource(res_data);
366 return 0;
369 typedef NTSTATUS (WINAPI *kernel_callback)(void *params, ULONG size);
370 static const kernel_callback kernel_callbacks[] =
372 macdrv_app_icon,
373 macdrv_app_quit_request,
374 macdrv_dnd_query_drag,
375 macdrv_dnd_query_drop,
376 macdrv_dnd_query_exited,
377 macdrv_ime_query_char_rect,
378 macdrv_ime_set_text,
381 C_ASSERT(NtUserDriverCallbackFirst + ARRAYSIZE(kernel_callbacks) == client_func_last);
384 static BOOL process_attach(void)
386 struct init_params params;
387 void **callback_table;
389 struct localized_string *str;
390 struct localized_string strings[] = {
391 { .id = STRING_MENU_WINE },
392 { .id = STRING_MENU_ITEM_HIDE_APPNAME },
393 { .id = STRING_MENU_ITEM_HIDE },
394 { .id = STRING_MENU_ITEM_HIDE_OTHERS },
395 { .id = STRING_MENU_ITEM_SHOW_ALL },
396 { .id = STRING_MENU_ITEM_QUIT_APPNAME },
397 { .id = STRING_MENU_ITEM_QUIT },
399 { .id = STRING_MENU_WINDOW },
400 { .id = STRING_MENU_ITEM_MINIMIZE },
401 { .id = STRING_MENU_ITEM_ZOOM },
402 { .id = STRING_MENU_ITEM_ENTER_FULL_SCREEN },
403 { .id = STRING_MENU_ITEM_BRING_ALL_TO_FRONT },
405 { .id = 0 }
408 for (str = strings; str->id; str++)
409 str->len = LoadStringW(macdrv_module, str->id, (WCHAR *)&str->str, 0);
410 params.strings = strings;
412 if (MACDRV_CALL(init, &params)) return FALSE;
414 callback_table = NtCurrentTeb()->Peb->KernelCallbackTable;
415 memcpy( callback_table + NtUserDriverCallbackFirst, kernel_callbacks, sizeof(kernel_callbacks) );
417 return TRUE;
420 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
422 if (reason != DLL_PROCESS_ATTACH) return TRUE;
424 DisableThreadLibraryCalls(instance);
425 macdrv_module = instance;
426 return process_attach();
429 int CDECL wine_notify_icon(DWORD msg, NOTIFYICONDATAW *data)
431 struct notify_icon_params params = { .msg = msg, .data = data };
432 return MACDRV_CALL(notify_icon, &params);