winemac: Handle a Cocoa quit request as a single-process session shutdown.
This lets programs ask the user to save documents, etc., and possibly cancel the quit.
This commit is contained in:
parent
ca2d7140fb
commit
fd1e187ac7
|
@ -1206,6 +1206,49 @@ - (void)applicationDidResignActive:(NSNotification *)notification
|
|||
macdrv_release_event(event);
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
NSApplicationTerminateReply ret = NSTerminateNow;
|
||||
NSAppleEventManager* m = [NSAppleEventManager sharedAppleEventManager];
|
||||
NSAppleEventDescriptor* desc = [m currentAppleEvent];
|
||||
macdrv_event* event;
|
||||
WineEventQueue* queue;
|
||||
|
||||
event = macdrv_create_event(APP_QUIT_REQUESTED, nil);
|
||||
event->deliver = 1;
|
||||
switch ([[desc attributeDescriptorForKeyword:kAEQuitReason] int32Value])
|
||||
{
|
||||
case kAELogOut:
|
||||
case kAEReallyLogOut:
|
||||
event->app_quit_requested.reason = QUIT_REASON_LOGOUT;
|
||||
break;
|
||||
case kAEShowRestartDialog:
|
||||
event->app_quit_requested.reason = QUIT_REASON_RESTART;
|
||||
break;
|
||||
case kAEShowShutdownDialog:
|
||||
event->app_quit_requested.reason = QUIT_REASON_SHUTDOWN;
|
||||
break;
|
||||
default:
|
||||
event->app_quit_requested.reason = QUIT_REASON_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
[eventQueuesLock lock];
|
||||
|
||||
if ([eventQueues count])
|
||||
{
|
||||
for (queue in eventQueues)
|
||||
[queue postEvent:event];
|
||||
ret = NSTerminateLater;
|
||||
}
|
||||
|
||||
[eventQueuesLock unlock];
|
||||
|
||||
macdrv_release_event(event);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)applicationWillFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
|
||||
|
@ -1541,3 +1584,13 @@ void macdrv_set_application_icon(CFArrayRef images)
|
|||
[NSApp setApplicationIconFromCGImageArray:imageArray];
|
||||
});
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* macdrv_quit_reply
|
||||
*/
|
||||
void macdrv_quit_reply(int reply)
|
||||
{
|
||||
OnMainThread(^{
|
||||
[NSApp replyToApplicationShouldTerminate:reply];
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ static const char *dbgstr_event(int type)
|
|||
{
|
||||
static const char * const event_names[] = {
|
||||
"APP_DEACTIVATED",
|
||||
"APP_QUIT_REQUESTED",
|
||||
"DISPLAYS_CHANGED",
|
||||
"KEY_PRESS",
|
||||
"KEY_RELEASE",
|
||||
|
@ -87,6 +88,7 @@ static macdrv_event_mask get_event_mask(DWORD mask)
|
|||
if (mask & QS_POSTMESSAGE)
|
||||
{
|
||||
event_mask |= event_mask_for_type(APP_DEACTIVATED);
|
||||
event_mask |= event_mask_for_type(APP_QUIT_REQUESTED);
|
||||
event_mask |= event_mask_for_type(DISPLAYS_CHANGED);
|
||||
event_mask |= event_mask_for_type(STATUS_ITEM_CLICKED);
|
||||
event_mask |= event_mask_for_type(WINDOW_CLOSE_REQUESTED);
|
||||
|
@ -165,6 +167,9 @@ void macdrv_handle_event(const macdrv_event *event)
|
|||
case APP_DEACTIVATED:
|
||||
macdrv_app_deactivated();
|
||||
break;
|
||||
case APP_QUIT_REQUESTED:
|
||||
macdrv_app_quit_requested(event);
|
||||
break;
|
||||
case DISPLAYS_CHANGED:
|
||||
macdrv_displays_changed(event);
|
||||
break;
|
||||
|
@ -257,7 +262,8 @@ DWORD CDECL macdrv_MsgWaitForMultipleObjectsEx(DWORD count, const HANDLE *handle
|
|||
timeout, flags & MWMO_ALERTABLE);
|
||||
}
|
||||
|
||||
if (data->current_event && data->current_event->type != QUERY_EVENT)
|
||||
if (data->current_event && data->current_event->type != QUERY_EVENT &&
|
||||
data->current_event->type != APP_QUIT_REQUESTED)
|
||||
event_mask = 0; /* don't process nested events */
|
||||
|
||||
if (process_events(data->queue, event_mask)) ret = count - 1;
|
||||
|
|
|
@ -149,6 +149,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
|
|||
extern void macdrv_window_got_focus(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_window_lost_focus(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_app_deactivated(void) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_app_quit_requested(const macdrv_event *event) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_window_did_minimize(HWND hwnd) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_window_did_unminimize(HWND hwnd) DECLSPEC_HIDDEN;
|
||||
|
||||
|
|
|
@ -134,6 +134,7 @@
|
|||
extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_beep(void) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_set_application_icon(CFArrayRef images) DECLSPEC_HIDDEN;
|
||||
extern void macdrv_quit_reply(int reply) DECLSPEC_HIDDEN;
|
||||
|
||||
|
||||
/* cursor */
|
||||
|
@ -153,6 +154,7 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
|
|||
/* event */
|
||||
enum {
|
||||
APP_DEACTIVATED,
|
||||
APP_QUIT_REQUESTED,
|
||||
DISPLAYS_CHANGED,
|
||||
KEY_PRESS,
|
||||
KEY_RELEASE,
|
||||
|
@ -172,6 +174,13 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
|
|||
NUM_EVENT_TYPES
|
||||
};
|
||||
|
||||
enum {
|
||||
QUIT_REASON_NONE,
|
||||
QUIT_REASON_LOGOUT,
|
||||
QUIT_REASON_RESTART,
|
||||
QUIT_REASON_SHUTDOWN,
|
||||
};
|
||||
|
||||
typedef uint32_t macdrv_event_mask;
|
||||
|
||||
typedef struct macdrv_event {
|
||||
|
@ -180,6 +189,9 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display,
|
|||
int type;
|
||||
macdrv_window window;
|
||||
union {
|
||||
struct {
|
||||
int reason;
|
||||
} app_quit_requested;
|
||||
struct {
|
||||
int activating;
|
||||
} displays_changed;
|
||||
|
|
|
@ -1637,3 +1637,170 @@ void macdrv_window_did_unminimize(HWND hwnd)
|
|||
done:
|
||||
release_win_data(data);
|
||||
}
|
||||
|
||||
|
||||
struct quit_info {
|
||||
HWND *wins;
|
||||
UINT capacity;
|
||||
UINT count;
|
||||
UINT done;
|
||||
DWORD flags;
|
||||
BOOL result;
|
||||
BOOL replied;
|
||||
};
|
||||
|
||||
|
||||
static BOOL CALLBACK get_process_windows(HWND hwnd, LPARAM lp)
|
||||
{
|
||||
struct quit_info *qi = (struct quit_info*)lp;
|
||||
DWORD pid;
|
||||
|
||||
GetWindowThreadProcessId(hwnd, &pid);
|
||||
if (pid == GetCurrentProcessId())
|
||||
{
|
||||
if (qi->count >= qi->capacity)
|
||||
{
|
||||
UINT new_cap = qi->capacity * 2;
|
||||
HWND *new_wins = HeapReAlloc(GetProcessHeap(), 0, qi->wins,
|
||||
new_cap * sizeof(*qi->wins));
|
||||
if (!new_wins) return FALSE;
|
||||
qi->wins = new_wins;
|
||||
qi->capacity = new_cap;
|
||||
}
|
||||
|
||||
qi->wins[qi->count++] = hwnd;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void CALLBACK quit_callback(HWND hwnd, UINT msg, ULONG_PTR data, LRESULT result)
|
||||
{
|
||||
struct quit_info *qi = (struct quit_info*)data;
|
||||
|
||||
qi->done++;
|
||||
|
||||
if (msg == WM_QUERYENDSESSION)
|
||||
{
|
||||
TRACE("got WM_QUERYENDSESSION result %ld from win %p (%u of %u done)\n", result,
|
||||
hwnd, qi->done, qi->count);
|
||||
|
||||
if (!result && qi->result)
|
||||
{
|
||||
qi->result = FALSE;
|
||||
|
||||
/* On the first FALSE from WM_QUERYENDSESSION, we already know the
|
||||
ultimate reply. Might as well tell Cocoa now. */
|
||||
if (!qi->replied)
|
||||
{
|
||||
qi->replied = TRUE;
|
||||
TRACE("giving quit reply %d\n", qi->result);
|
||||
macdrv_quit_reply(qi->result);
|
||||
}
|
||||
}
|
||||
|
||||
if (qi->done >= qi->count)
|
||||
{
|
||||
UINT i;
|
||||
|
||||
qi->done = 0;
|
||||
for (i = 0; i < qi->count; i++)
|
||||
{
|
||||
TRACE("sending WM_ENDSESSION to win %p result %d flags 0x%08x\n", qi->wins[i],
|
||||
qi->result, qi->flags);
|
||||
if (!SendMessageCallbackW(qi->wins[i], WM_ENDSESSION, qi->result, qi->flags,
|
||||
quit_callback, (ULONG_PTR)qi))
|
||||
{
|
||||
WARN("failed to send WM_ENDSESSION to win %p; error 0x%08x\n",
|
||||
qi->wins[i], GetLastError());
|
||||
quit_callback(qi->wins[i], WM_ENDSESSION, (ULONG_PTR)qi, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* WM_ENDSESSION */
|
||||
{
|
||||
TRACE("finished WM_ENDSESSION for win %p (%u of %u done)\n", hwnd, qi->done, qi->count);
|
||||
|
||||
if (qi->done >= qi->count)
|
||||
{
|
||||
if (!qi->replied)
|
||||
{
|
||||
TRACE("giving quit reply %d\n", qi->result);
|
||||
macdrv_quit_reply(qi->result);
|
||||
}
|
||||
|
||||
TRACE("%sterminating process\n", qi->result ? "" : "not ");
|
||||
if (qi->result)
|
||||
TerminateProcess(GetCurrentProcess(), 0);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, qi->wins);
|
||||
HeapFree(GetProcessHeap(), 0, qi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* macdrv_app_quit_requested
|
||||
*
|
||||
* Handler for APP_QUIT_REQUESTED events.
|
||||
*/
|
||||
void macdrv_app_quit_requested(const macdrv_event *event)
|
||||
{
|
||||
struct quit_info *qi;
|
||||
UINT i;
|
||||
|
||||
TRACE("reason %d\n", event->app_quit_requested.reason);
|
||||
|
||||
qi = HeapAlloc(GetProcessHeap(), 0, sizeof(*qi));
|
||||
if (!qi)
|
||||
goto fail;
|
||||
|
||||
qi->capacity = 32;
|
||||
qi->wins = HeapAlloc(GetProcessHeap(), 0, qi->capacity * sizeof(*qi->wins));
|
||||
qi->count = qi->done = 0;
|
||||
|
||||
if (!qi->wins || !EnumWindows(get_process_windows, (LPARAM)qi))
|
||||
goto fail;
|
||||
|
||||
switch (event->app_quit_requested.reason)
|
||||
{
|
||||
case QUIT_REASON_LOGOUT:
|
||||
default:
|
||||
qi->flags = ENDSESSION_LOGOFF;
|
||||
break;
|
||||
case QUIT_REASON_RESTART:
|
||||
case QUIT_REASON_SHUTDOWN:
|
||||
qi->flags = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
qi->result = TRUE;
|
||||
qi->replied = FALSE;
|
||||
|
||||
for (i = 0; i < qi->count; i++)
|
||||
{
|
||||
TRACE("sending WM_QUERYENDSESSION to win %p\n", qi->wins[i]);
|
||||
if (!SendMessageCallbackW(qi->wins[i], WM_QUERYENDSESSION, 0, qi->flags,
|
||||
quit_callback, (ULONG_PTR)qi))
|
||||
{
|
||||
WARN("failed to send WM_QUERYENDSESSION to win %p; error 0x%08x; assuming refusal\n",
|
||||
qi->wins[i], GetLastError());
|
||||
quit_callback(qi->wins[i], WM_QUERYENDSESSION, (ULONG_PTR)qi, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/* quit_callback() will clean up qi */
|
||||
return;
|
||||
|
||||
fail:
|
||||
WARN("failed to allocate window list\n");
|
||||
if (qi)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, qi->wins);
|
||||
HeapFree(GetProcessHeap(), 0, qi);
|
||||
}
|
||||
macdrv_quit_reply(FALSE);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue