"Fossies" - the Fresh Open Source Software Archive

Member "wine-6.0.1/dlls/winemac.drv/systray.c" (7 Jun 2021, 11741 Bytes) of package /linux/misc/wine-6.0.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "systray.c": 6.0_vs_6.0.1.

    1 /*
    2  * Mac driver system tray management
    3  *
    4  * Copyright (C) 2004 Mike Hearn, for CodeWeavers
    5  * Copyright (C) 2005 Robert Shearman
    6  * Copyright (C) 2008 Alexandre Julliard
    7  * Copyright (C) 2012, 2013 Ken Thomases for CodeWeavers Inc.
    8  *
    9  * This library is free software; you can redistribute it and/or
   10  * modify it under the terms of the GNU Lesser General Public
   11  * License as published by the Free Software Foundation; either
   12  * version 2.1 of the License, or (at your option) any later version.
   13  *
   14  * This library is distributed in the hope that it will be useful,
   15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   17  * Lesser General Public License for more details.
   18  *
   19  * You should have received a copy of the GNU Lesser General Public
   20  * License along with this library; if not, write to the Free Software
   21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   22  */
   23 
   24 #include "config.h"
   25 
   26 #include "macdrv.h"
   27 
   28 #include "windef.h"
   29 #include "winuser.h"
   30 #include "shellapi.h"
   31 
   32 #include "wine/list.h"
   33 #include "wine/debug.h"
   34 
   35 WINE_DEFAULT_DEBUG_CHANNEL(systray);
   36 
   37 
   38 /* an individual systray icon */
   39 struct tray_icon
   40 {
   41     struct list         entry;
   42     HWND                owner;              /* the HWND passed in to the Shell_NotifyIcon call */
   43     UINT                id;                 /* the unique id given by the app */
   44     UINT                callback_message;
   45     HICON               image;              /* the image to render */
   46     WCHAR               tiptext[128];       /* tooltip text */
   47     DWORD               state;              /* state flags */
   48     macdrv_status_item  status_item;
   49     UINT                version;
   50 };
   51 
   52 static struct list icon_list = LIST_INIT(icon_list);
   53 
   54 
   55 static BOOL delete_icon(struct tray_icon *icon);
   56 
   57 
   58 /***********************************************************************
   59  *              cleanup_icons
   60  *
   61  * Delete all systray icons owned by a given window.
   62  */
   63 static void cleanup_icons(HWND hwnd)
   64 {
   65     struct tray_icon *icon, *next;
   66 
   67     LIST_FOR_EACH_ENTRY_SAFE(icon, next, &icon_list, struct tray_icon, entry)
   68         if (icon->owner == hwnd) delete_icon(icon);
   69 }
   70 
   71 
   72 /***********************************************************************
   73  *              get_icon
   74  *
   75  * Retrieves an icon record by owner window and ID.
   76  */
   77 static struct tray_icon *get_icon(HWND owner, UINT id)
   78 {
   79     struct tray_icon *this;
   80 
   81     LIST_FOR_EACH_ENTRY(this, &icon_list, struct tray_icon, entry)
   82         if ((this->id == id) && (this->owner == owner)) return this;
   83     return NULL;
   84 }
   85 
   86 
   87 /***********************************************************************
   88  *              modify_icon
   89  *
   90  * Modifies an existing tray icon and updates its status item as needed.
   91  */
   92 static BOOL modify_icon(struct tray_icon *icon, NOTIFYICONDATAW *nid)
   93 {
   94     BOOL update_image = FALSE, update_tooltip = FALSE;
   95 
   96     TRACE("hwnd %p id 0x%x flags %x\n", nid->hWnd, nid->uID, nid->uFlags);
   97 
   98     if (nid->uFlags & NIF_STATE)
   99     {
  100         DWORD changed = (icon->state ^ nid->dwState) & nid->dwStateMask;
  101         icon->state = (icon->state & ~nid->dwStateMask) | (nid->dwState & nid->dwStateMask);
  102         if (changed & NIS_HIDDEN)
  103         {
  104             if (icon->state & NIS_HIDDEN)
  105             {
  106                 if (icon->status_item)
  107                 {
  108                     TRACE("destroying status item %p\n", icon->status_item);
  109                     macdrv_destroy_status_item(icon->status_item);
  110                     icon->status_item = NULL;
  111                 }
  112             }
  113             else
  114             {
  115                 if (!icon->status_item)
  116                 {
  117                     struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
  118 
  119                     icon->status_item = macdrv_create_status_item(thread_data->queue);
  120                     if (icon->status_item)
  121                     {
  122                         TRACE("created status item %p\n", icon->status_item);
  123 
  124                         if (icon->image)
  125                             update_image = TRUE;
  126                         if (*icon->tiptext)
  127                             update_tooltip = TRUE;
  128                     }
  129                     else
  130                         WARN("failed to create status item\n");
  131                 }
  132             }
  133         }
  134     }
  135 
  136     if (nid->uFlags & NIF_ICON)
  137     {
  138         if (icon->image) DestroyIcon(icon->image);
  139         icon->image = CopyIcon(nid->hIcon);
  140         if (icon->status_item)
  141             update_image = TRUE;
  142     }
  143 
  144     if (nid->uFlags & NIF_MESSAGE)
  145     {
  146         icon->callback_message = nid->uCallbackMessage;
  147     }
  148     if (nid->uFlags & NIF_TIP)
  149     {
  150         lstrcpynW(icon->tiptext, nid->szTip, ARRAY_SIZE(icon->tiptext));
  151         if (icon->status_item)
  152             update_tooltip = TRUE;
  153     }
  154 
  155     if (update_image)
  156     {
  157         CGImageRef cgimage = NULL;
  158         if (icon->image)
  159             cgimage = create_cgimage_from_icon(icon->image, 0, 0);
  160         macdrv_set_status_item_image(icon->status_item, cgimage);
  161         CGImageRelease(cgimage);
  162     }
  163 
  164     if (update_tooltip)
  165     {
  166         CFStringRef s;
  167 
  168         TRACE("setting tooltip text for status item %p to %s\n", icon->status_item,
  169               debugstr_w(icon->tiptext));
  170         s = CFStringCreateWithCharacters(NULL, (UniChar*)icon->tiptext,
  171                                          lstrlenW(icon->tiptext));
  172         macdrv_set_status_item_tooltip(icon->status_item, s);
  173         CFRelease(s);
  174     }
  175 
  176     return TRUE;
  177 }
  178 
  179 
  180 /***********************************************************************
  181  *              add_icon
  182  *
  183  * Creates a new tray icon structure and adds it to the list.
  184  */
  185 static BOOL add_icon(NOTIFYICONDATAW *nid)
  186 {
  187     NOTIFYICONDATAW new_nid;
  188     struct tray_icon *icon;
  189 
  190     TRACE("hwnd %p id 0x%x\n", nid->hWnd, nid->uID);
  191 
  192     if ((icon = get_icon(nid->hWnd, nid->uID)))
  193     {
  194         WARN("duplicate tray icon add, buggy app?\n");
  195         return FALSE;
  196     }
  197 
  198     if (!(icon = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*icon))))
  199     {
  200         ERR("out of memory\n");
  201         return FALSE;
  202     }
  203 
  204     icon->id     = nid->uID;
  205     icon->owner  = nid->hWnd;
  206     icon->state  = NIS_HIDDEN;
  207 
  208     list_add_tail(&icon_list, &icon->entry);
  209 
  210     if (!(nid->uFlags & NIF_STATE) || !(nid->dwStateMask & NIS_HIDDEN))
  211     {
  212         new_nid = *nid;
  213         new_nid.uFlags |= NIF_STATE;
  214         new_nid.dwState     &= ~NIS_HIDDEN;
  215         new_nid.dwStateMask |= NIS_HIDDEN;
  216         nid = &new_nid;
  217     }
  218     return modify_icon(icon, nid);
  219 }
  220 
  221 
  222 /***********************************************************************
  223  *              delete_icon
  224  *
  225  * Destroy tray icon status item and delete structure.
  226  */
  227 static BOOL delete_icon(struct tray_icon *icon)
  228 {
  229     TRACE("hwnd %p id 0x%x\n", icon->owner, icon->id);
  230 
  231     if (icon->status_item)
  232     {
  233         TRACE("destroying status item %p\n", icon->status_item);
  234         macdrv_destroy_status_item(icon->status_item);
  235     }
  236     list_remove(&icon->entry);
  237     DestroyIcon(icon->image);
  238     HeapFree(GetProcessHeap(), 0, icon);
  239     return TRUE;
  240 }
  241 
  242 
  243 /***********************************************************************
  244  *              wine_notify_icon   (MACDRV.@)
  245  *
  246  * Driver-side implementation of Shell_NotifyIcon.
  247  */
  248 int CDECL wine_notify_icon(DWORD msg, NOTIFYICONDATAW *data)
  249 {
  250     BOOL ret = FALSE;
  251     struct tray_icon *icon;
  252 
  253     switch (msg)
  254     {
  255     case NIM_ADD:
  256         ret = add_icon(data);
  257         break;
  258     case NIM_DELETE:
  259         if ((icon = get_icon(data->hWnd, data->uID))) ret = delete_icon(icon);
  260         break;
  261     case NIM_MODIFY:
  262         if ((icon = get_icon(data->hWnd, data->uID))) ret = modify_icon(icon, data);
  263         break;
  264     case 0xdead:  /* Wine extension: owner window has died */
  265         cleanup_icons(data->hWnd);
  266         break;
  267     case NIM_SETVERSION:
  268         if ((icon = get_icon(data->hWnd, data->uID)))
  269         {
  270             icon->version = data->uVersion;
  271             ret = TRUE;
  272         }
  273         break;
  274     default:
  275         FIXME("unhandled tray message: %u\n", msg);
  276         break;
  277     }
  278     return ret;
  279 }
  280 
  281 static BOOL notify_owner(struct tray_icon *icon, UINT msg, int x, int y)
  282 {
  283     WPARAM wp = icon->id;
  284     LPARAM lp = msg;
  285 
  286     if (icon->version >= NOTIFY_VERSION_4)
  287     {
  288         wp = MAKEWPARAM(x, y);
  289         lp = MAKELPARAM(msg, icon->id);
  290     }
  291 
  292     TRACE("posting msg 0x%04x to hwnd %p id 0x%x\n", msg, icon->owner, icon->id);
  293     if (!SendNotifyMessageW(icon->owner, icon->callback_message, wp, lp) &&
  294         (GetLastError() == ERROR_INVALID_WINDOW_HANDLE))
  295     {
  296         WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id);
  297         delete_icon(icon);
  298         return FALSE;
  299     }
  300     return TRUE;
  301 }
  302 
  303 /***********************************************************************
  304  *              macdrv_status_item_mouse_button
  305  *
  306  * Handle STATUS_ITEM_MOUSE_BUTTON events.
  307  */
  308 void macdrv_status_item_mouse_button(const macdrv_event *event)
  309 {
  310     struct tray_icon *icon;
  311 
  312     TRACE("item %p button %d down %d count %d pos %d,%d\n", event->status_item_mouse_button.item,
  313           event->status_item_mouse_button.button, event->status_item_mouse_button.down,
  314           event->status_item_mouse_button.count, event->status_item_mouse_button.x,
  315           event->status_item_mouse_button.y);
  316 
  317     LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry)
  318     {
  319         if (icon->status_item == event->status_item_mouse_button.item)
  320         {
  321             UINT msg;
  322 
  323             switch (event->status_item_mouse_button.button)
  324             {
  325                 case 0: msg = WM_LBUTTONDOWN; break;
  326                 case 1: msg = WM_RBUTTONDOWN; break;
  327                 case 2: msg = WM_MBUTTONDOWN; break;
  328                 default:
  329                     TRACE("ignoring button beyond the third\n");
  330                     return;
  331             }
  332 
  333             if (!event->status_item_mouse_button.down)
  334                 msg += WM_LBUTTONUP - WM_LBUTTONDOWN;
  335             else if (event->status_item_mouse_button.count % 2 == 0)
  336                 msg += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN;
  337 
  338             if (!SendMessageW(icon->owner, WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS, 0, 0) &&
  339                 GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
  340             {
  341                 WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id);
  342                 delete_icon(icon);
  343                 return;
  344             }
  345 
  346             if (!notify_owner(icon, msg, event->status_item_mouse_button.x, event->status_item_mouse_button.y))
  347                 return;
  348 
  349             if (icon->version)
  350             {
  351                 if (msg == WM_LBUTTONUP)
  352                     notify_owner(icon, NIN_SELECT, event->status_item_mouse_button.x, event->status_item_mouse_button.y);
  353                 else if (msg == WM_RBUTTONUP)
  354                     notify_owner(icon, WM_CONTEXTMENU, event->status_item_mouse_button.x, event->status_item_mouse_button.y);
  355             }
  356 
  357             break;
  358         }
  359     }
  360 }
  361 
  362 
  363 /***********************************************************************
  364  *              macdrv_status_item_mouse_move
  365  *
  366  * Handle STATUS_ITEM_MOUSE_MOVE events.
  367  */
  368 void macdrv_status_item_mouse_move(const macdrv_event *event)
  369 {
  370     struct tray_icon *icon;
  371 
  372     TRACE("item %p pos %d,%d\n", event->status_item_mouse_move.item,
  373           event->status_item_mouse_move.x, event->status_item_mouse_move.y);
  374 
  375     LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry)
  376     {
  377         if (icon->status_item == event->status_item_mouse_move.item)
  378         {
  379             notify_owner(icon, WM_MOUSEMOVE, event->status_item_mouse_move.x, event->status_item_mouse_move.y);
  380             break;
  381         }
  382     }
  383 }