notificationicon.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. # Pure ctypes windows taskbar notification icon
  2. # via https://gist.github.com/jasonbot/5759510
  3. # Modified for ZeroNet
  4. import ctypes
  5. import ctypes.wintypes
  6. import os
  7. #import threading
  8. #import Queue
  9. import uuid
  10. import time
  11. import gevent
  12. __all__ = ['NotificationIcon']
  13. # Create popup menu
  14. CreatePopupMenu = ctypes.windll.user32.CreatePopupMenu
  15. CreatePopupMenu.restype = ctypes.wintypes.HMENU
  16. CreatePopupMenu.argtypes = []
  17. MF_BYCOMMAND = 0x0
  18. MF_BYPOSITION = 0x400
  19. MF_BITMAP = 0x4
  20. MF_CHECKED = 0x8
  21. MF_DISABLED = 0x2
  22. MF_ENABLED = 0x0
  23. MF_GRAYED = 0x1
  24. MF_MENUBARBREAK = 0x20
  25. MF_MENUBREAK = 0x40
  26. MF_OWNERDRAW = 0x100
  27. MF_POPUP = 0x10
  28. MF_SEPARATOR = 0x800
  29. MF_STRING = 0x0
  30. MF_UNCHECKED = 0x0
  31. InsertMenu = ctypes.windll.user32.InsertMenuW
  32. InsertMenu.restype = ctypes.wintypes.BOOL
  33. InsertMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.LPCWSTR]
  34. AppendMenu = ctypes.windll.user32.AppendMenuW
  35. AppendMenu.restype = ctypes.wintypes.BOOL
  36. AppendMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.LPCWSTR]
  37. SetMenuDefaultItem = ctypes.windll.user32.SetMenuDefaultItem
  38. SetMenuDefaultItem.restype = ctypes.wintypes.BOOL
  39. SetMenuDefaultItem.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT]
  40. #class MENUITEMINFO(ctypes.Structure):
  41. # UINT cbSize;
  42. # UINT fMask;
  43. # UINT fType;
  44. # UINT fState;
  45. # UINT wID;
  46. # HMENU hSubMenu;
  47. # HBITMAP hbmpChecked;
  48. # HBITMAP hbmpUnchecked;
  49. # ULONG_PTR dwItemData;
  50. # LPTSTR dwTypeData;
  51. # UINT cch;
  52. # HBITMAP hbmpItem;
  53. #
  54. #BOOL WINAPI InsertMenuItem(
  55. # __in HMENU hMenu,
  56. # __in UINT uItem,
  57. # __in BOOL fByPosition,
  58. # __in LPCMENUITEMINFO lpmii
  59. #);
  60. #
  61. class POINT(ctypes.Structure):
  62. _fields_ = [ ('x', ctypes.wintypes.LONG),
  63. ('y', ctypes.wintypes.LONG)]
  64. GetCursorPos = ctypes.windll.user32.GetCursorPos
  65. GetCursorPos.argtypes = [ctypes.POINTER(POINT)]
  66. SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
  67. SetForegroundWindow.argtypes = [ctypes.wintypes.HWND]
  68. TPM_LEFTALIGN = 0x0
  69. TPM_CENTERALIGN = 0x4
  70. TPM_RIGHTALIGN = 0x8
  71. TPM_TOPALIGN = 0x0
  72. TPM_VCENTERALIGN = 0x10
  73. TPM_BOTTOMALIGN = 0x20
  74. TPM_NONOTIFY = 0x80
  75. TPM_RETURNCMD = 0x100
  76. TPM_LEFTBUTTON = 0x0
  77. TPM_RIGHTBUTTON = 0x2
  78. TPM_HORNEGANIMATION = 0x800
  79. TPM_HORPOSANIMATION = 0x400
  80. TPM_NOANIMATION = 0x4000
  81. TPM_VERNEGANIMATION = 0x2000
  82. TPM_VERPOSANIMATION = 0x1000
  83. TrackPopupMenu = ctypes.windll.user32.TrackPopupMenu
  84. TrackPopupMenu.restype = ctypes.wintypes.BOOL
  85. TrackPopupMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.wintypes.HWND, ctypes.c_void_p]
  86. PostMessage = ctypes.windll.user32.PostMessageW
  87. PostMessage.restype = ctypes.wintypes.BOOL
  88. PostMessage.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.UINT, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM]
  89. DestroyMenu = ctypes.windll.user32.DestroyMenu
  90. DestroyMenu.restype = ctypes.wintypes.BOOL
  91. DestroyMenu.argtypes = [ctypes.wintypes.HMENU]
  92. # Create notification icon
  93. GUID = ctypes.c_ubyte * 16
  94. class TimeoutVersionUnion(ctypes.Union):
  95. _fields_ = [('uTimeout', ctypes.wintypes.UINT),
  96. ('uVersion', ctypes.wintypes.UINT),]
  97. NIS_HIDDEN = 0x1
  98. NIS_SHAREDICON = 0x2
  99. class NOTIFYICONDATA(ctypes.Structure):
  100. def __init__(self, *args, **kwargs):
  101. super(NOTIFYICONDATA, self).__init__(*args, **kwargs)
  102. self.cbSize = ctypes.sizeof(self)
  103. _fields_ = [
  104. ('cbSize', ctypes.wintypes.DWORD),
  105. ('hWnd', ctypes.wintypes.HWND),
  106. ('uID', ctypes.wintypes.UINT),
  107. ('uFlags', ctypes.wintypes.UINT),
  108. ('uCallbackMessage', ctypes.wintypes.UINT),
  109. ('hIcon', ctypes.wintypes.HICON),
  110. ('szTip', ctypes.wintypes.WCHAR * 64),
  111. ('dwState', ctypes.wintypes.DWORD),
  112. ('dwStateMask', ctypes.wintypes.DWORD),
  113. ('szInfo', ctypes.wintypes.WCHAR * 256),
  114. ('union', TimeoutVersionUnion),
  115. ('szInfoTitle', ctypes.wintypes.WCHAR * 64),
  116. ('dwInfoFlags', ctypes.wintypes.DWORD),
  117. ('guidItem', GUID),
  118. ('hBalloonIcon', ctypes.wintypes.HICON),
  119. ]
  120. NIM_ADD = 0
  121. NIM_MODIFY = 1
  122. NIM_DELETE = 2
  123. NIM_SETFOCUS = 3
  124. NIM_SETVERSION = 4
  125. NIF_MESSAGE = 1
  126. NIF_ICON = 2
  127. NIF_TIP = 4
  128. NIF_STATE = 8
  129. NIF_INFO = 16
  130. NIF_GUID = 32
  131. NIF_REALTIME = 64
  132. NIF_SHOWTIP = 128
  133. NIIF_NONE = 0
  134. NIIF_INFO = 1
  135. NIIF_WARNING = 2
  136. NIIF_ERROR = 3
  137. NIIF_USER = 4
  138. NOTIFYICON_VERSION = 3
  139. NOTIFYICON_VERSION_4 = 4
  140. Shell_NotifyIcon = ctypes.windll.shell32.Shell_NotifyIconW
  141. Shell_NotifyIcon.restype = ctypes.wintypes.BOOL
  142. Shell_NotifyIcon.argtypes = [ctypes.wintypes.DWORD, ctypes.POINTER(NOTIFYICONDATA)]
  143. # Load icon/image
  144. IMAGE_BITMAP = 0
  145. IMAGE_ICON = 1
  146. IMAGE_CURSOR = 2
  147. LR_CREATEDIBSECTION = 0x00002000
  148. LR_DEFAULTCOLOR = 0x00000000
  149. LR_DEFAULTSIZE = 0x00000040
  150. LR_LOADFROMFILE = 0x00000010
  151. LR_LOADMAP3DCOLORS = 0x00001000
  152. LR_LOADTRANSPARENT = 0x00000020
  153. LR_MONOCHROME = 0x00000001
  154. LR_SHARED = 0x00008000
  155. LR_VGACOLOR = 0x00000080
  156. OIC_SAMPLE = 32512
  157. OIC_HAND = 32513
  158. OIC_QUES = 32514
  159. OIC_BANG = 32515
  160. OIC_NOTE = 32516
  161. OIC_WINLOGO = 32517
  162. OIC_WARNING = OIC_BANG
  163. OIC_ERROR = OIC_HAND
  164. OIC_INFORMATION = OIC_NOTE
  165. LoadImage = ctypes.windll.user32.LoadImageW
  166. LoadImage.restype = ctypes.wintypes.HANDLE
  167. LoadImage.argtypes = [ctypes.wintypes.HINSTANCE, ctypes.wintypes.LPCWSTR, ctypes.wintypes.UINT, ctypes.c_int, ctypes.c_int, ctypes.wintypes.UINT]
  168. # CreateWindow call
  169. WNDPROC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.wintypes.HWND, ctypes.c_uint, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM)
  170. DefWindowProc = ctypes.windll.user32.DefWindowProcW
  171. DefWindowProc.restype = ctypes.c_int
  172. DefWindowProc.argtypes = [ctypes.wintypes.HWND, ctypes.c_uint, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM]
  173. WS_OVERLAPPED = 0x00000000L
  174. WS_POPUP = 0x80000000L
  175. WS_CHILD = 0x40000000L
  176. WS_MINIMIZE = 0x20000000L
  177. WS_VISIBLE = 0x10000000L
  178. WS_DISABLED = 0x08000000L
  179. WS_CLIPSIBLINGS = 0x04000000L
  180. WS_CLIPCHILDREN = 0x02000000L
  181. WS_MAXIMIZE = 0x01000000L
  182. WS_CAPTION = 0x00C00000L
  183. WS_BORDER = 0x00800000L
  184. WS_DLGFRAME = 0x00400000L
  185. WS_VSCROLL = 0x00200000L
  186. WS_HSCROLL = 0x00100000L
  187. WS_SYSMENU = 0x00080000L
  188. WS_THICKFRAME = 0x00040000L
  189. WS_GROUP = 0x00020000L
  190. WS_TABSTOP = 0x00010000L
  191. WS_MINIMIZEBOX = 0x00020000L
  192. WS_MAXIMIZEBOX = 0x00010000L
  193. WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED |
  194. WS_CAPTION |
  195. WS_SYSMENU |
  196. WS_THICKFRAME |
  197. WS_MINIMIZEBOX |
  198. WS_MAXIMIZEBOX)
  199. SM_XVIRTUALSCREEN = 76
  200. SM_YVIRTUALSCREEN = 77
  201. SM_CXVIRTUALSCREEN = 78
  202. SM_CYVIRTUALSCREEN = 79
  203. SM_CMONITORS = 80
  204. SM_SAMEDISPLAYFORMAT = 81
  205. WM_NULL = 0x0000
  206. WM_CREATE = 0x0001
  207. WM_DESTROY = 0x0002
  208. WM_MOVE = 0x0003
  209. WM_SIZE = 0x0005
  210. WM_ACTIVATE = 0x0006
  211. WM_SETFOCUS = 0x0007
  212. WM_KILLFOCUS = 0x0008
  213. WM_ENABLE = 0x000A
  214. WM_SETREDRAW = 0x000B
  215. WM_SETTEXT = 0x000C
  216. WM_GETTEXT = 0x000D
  217. WM_GETTEXTLENGTH = 0x000E
  218. WM_PAINT = 0x000F
  219. WM_CLOSE = 0x0010
  220. WM_QUERYENDSESSION = 0x0011
  221. WM_QUIT = 0x0012
  222. WM_QUERYOPEN = 0x0013
  223. WM_ERASEBKGND = 0x0014
  224. WM_SYSCOLORCHANGE = 0x0015
  225. WM_ENDSESSION = 0x0016
  226. WM_SHOWWINDOW = 0x0018
  227. WM_CTLCOLOR = 0x0019
  228. WM_WININICHANGE = 0x001A
  229. WM_SETTINGCHANGE = 0x001A
  230. WM_DEVMODECHANGE = 0x001B
  231. WM_ACTIVATEAPP = 0x001C
  232. WM_FONTCHANGE = 0x001D
  233. WM_TIMECHANGE = 0x001E
  234. WM_CANCELMODE = 0x001F
  235. WM_SETCURSOR = 0x0020
  236. WM_MOUSEACTIVATE = 0x0021
  237. WM_CHILDACTIVATE = 0x0022
  238. WM_QUEUESYNC = 0x0023
  239. WM_GETMINMAXINFO = 0x0024
  240. WM_PAINTICON = 0x0026
  241. WM_ICONERASEBKGND = 0x0027
  242. WM_NEXTDLGCTL = 0x0028
  243. WM_SPOOLERSTATUS = 0x002A
  244. WM_DRAWITEM = 0x002B
  245. WM_MEASUREITEM = 0x002C
  246. WM_DELETEITEM = 0x002D
  247. WM_VKEYTOITEM = 0x002E
  248. WM_CHARTOITEM = 0x002F
  249. WM_SETFONT = 0x0030
  250. WM_GETFONT = 0x0031
  251. WM_SETHOTKEY = 0x0032
  252. WM_GETHOTKEY = 0x0033
  253. WM_QUERYDRAGICON = 0x0037
  254. WM_COMPAREITEM = 0x0039
  255. WM_GETOBJECT = 0x003D
  256. WM_COMPACTING = 0x0041
  257. WM_COMMNOTIFY = 0x0044
  258. WM_WINDOWPOSCHANGING = 0x0046
  259. WM_WINDOWPOSCHANGED = 0x0047
  260. WM_POWER = 0x0048
  261. WM_COPYDATA = 0x004A
  262. WM_CANCELJOURNAL = 0x004B
  263. WM_NOTIFY = 0x004E
  264. WM_INPUTLANGCHANGEREQUEST = 0x0050
  265. WM_INPUTLANGCHANGE = 0x0051
  266. WM_TCARD = 0x0052
  267. WM_HELP = 0x0053
  268. WM_USERCHANGED = 0x0054
  269. WM_NOTIFYFORMAT = 0x0055
  270. WM_CONTEXTMENU = 0x007B
  271. WM_STYLECHANGING = 0x007C
  272. WM_STYLECHANGED = 0x007D
  273. WM_DISPLAYCHANGE = 0x007E
  274. WM_GETICON = 0x007F
  275. WM_SETICON = 0x0080
  276. WM_NCCREATE = 0x0081
  277. WM_NCDESTROY = 0x0082
  278. WM_NCCALCSIZE = 0x0083
  279. WM_NCHITTEST = 0x0084
  280. WM_NCPAINT = 0x0085
  281. WM_NCACTIVATE = 0x0086
  282. WM_GETDLGCODE = 0x0087
  283. WM_SYNCPAINT = 0x0088
  284. WM_NCMOUSEMOVE = 0x00A0
  285. WM_NCLBUTTONDOWN = 0x00A1
  286. WM_NCLBUTTONUP = 0x00A2
  287. WM_NCLBUTTONDBLCLK = 0x00A3
  288. WM_NCRBUTTONDOWN = 0x00A4
  289. WM_NCRBUTTONUP = 0x00A5
  290. WM_NCRBUTTONDBLCLK = 0x00A6
  291. WM_NCMBUTTONDOWN = 0x00A7
  292. WM_NCMBUTTONUP = 0x00A8
  293. WM_NCMBUTTONDBLCLK = 0x00A9
  294. WM_KEYDOWN = 0x0100
  295. WM_KEYUP = 0x0101
  296. WM_CHAR = 0x0102
  297. WM_DEADCHAR = 0x0103
  298. WM_SYSKEYDOWN = 0x0104
  299. WM_SYSKEYUP = 0x0105
  300. WM_SYSCHAR = 0x0106
  301. WM_SYSDEADCHAR = 0x0107
  302. WM_KEYLAST = 0x0108
  303. WM_IME_STARTCOMPOSITION = 0x010D
  304. WM_IME_ENDCOMPOSITION = 0x010E
  305. WM_IME_COMPOSITION = 0x010F
  306. WM_IME_KEYLAST = 0x010F
  307. WM_INITDIALOG = 0x0110
  308. WM_COMMAND = 0x0111
  309. WM_SYSCOMMAND = 0x0112
  310. WM_TIMER = 0x0113
  311. WM_HSCROLL = 0x0114
  312. WM_VSCROLL = 0x0115
  313. WM_INITMENU = 0x0116
  314. WM_INITMENUPOPUP = 0x0117
  315. WM_MENUSELECT = 0x011F
  316. WM_MENUCHAR = 0x0120
  317. WM_ENTERIDLE = 0x0121
  318. WM_MENURBUTTONUP = 0x0122
  319. WM_MENUDRAG = 0x0123
  320. WM_MENUGETOBJECT = 0x0124
  321. WM_UNINITMENUPOPUP = 0x0125
  322. WM_MENUCOMMAND = 0x0126
  323. WM_CTLCOLORMSGBOX = 0x0132
  324. WM_CTLCOLOREDIT = 0x0133
  325. WM_CTLCOLORLISTBOX = 0x0134
  326. WM_CTLCOLORBTN = 0x0135
  327. WM_CTLCOLORDLG = 0x0136
  328. WM_CTLCOLORSCROLLBAR = 0x0137
  329. WM_CTLCOLORSTATIC = 0x0138
  330. WM_MOUSEMOVE = 0x0200
  331. WM_LBUTTONDOWN = 0x0201
  332. WM_LBUTTONUP = 0x0202
  333. WM_LBUTTONDBLCLK = 0x0203
  334. WM_RBUTTONDOWN = 0x0204
  335. WM_RBUTTONUP = 0x0205
  336. WM_RBUTTONDBLCLK = 0x0206
  337. WM_MBUTTONDOWN = 0x0207
  338. WM_MBUTTONUP = 0x0208
  339. WM_MBUTTONDBLCLK = 0x0209
  340. WM_MOUSEWHEEL = 0x020A
  341. WM_PARENTNOTIFY = 0x0210
  342. WM_ENTERMENULOOP = 0x0211
  343. WM_EXITMENULOOP = 0x0212
  344. WM_NEXTMENU = 0x0213
  345. WM_SIZING = 0x0214
  346. WM_CAPTURECHANGED = 0x0215
  347. WM_MOVING = 0x0216
  348. WM_DEVICECHANGE = 0x0219
  349. WM_MDICREATE = 0x0220
  350. WM_MDIDESTROY = 0x0221
  351. WM_MDIACTIVATE = 0x0222
  352. WM_MDIRESTORE = 0x0223
  353. WM_MDINEXT = 0x0224
  354. WM_MDIMAXIMIZE = 0x0225
  355. WM_MDITILE = 0x0226
  356. WM_MDICASCADE = 0x0227
  357. WM_MDIICONARRANGE = 0x0228
  358. WM_MDIGETACTIVE = 0x0229
  359. WM_MDISETMENU = 0x0230
  360. WM_ENTERSIZEMOVE = 0x0231
  361. WM_EXITSIZEMOVE = 0x0232
  362. WM_DROPFILES = 0x0233
  363. WM_MDIREFRESHMENU = 0x0234
  364. WM_IME_SETCONTEXT = 0x0281
  365. WM_IME_NOTIFY = 0x0282
  366. WM_IME_CONTROL = 0x0283
  367. WM_IME_COMPOSITIONFULL = 0x0284
  368. WM_IME_SELECT = 0x0285
  369. WM_IME_CHAR = 0x0286
  370. WM_IME_REQUEST = 0x0288
  371. WM_IME_KEYDOWN = 0x0290
  372. WM_IME_KEYUP = 0x0291
  373. WM_MOUSEHOVER = 0x02A1
  374. WM_MOUSELEAVE = 0x02A3
  375. WM_CUT = 0x0300
  376. WM_COPY = 0x0301
  377. WM_PASTE = 0x0302
  378. WM_CLEAR = 0x0303
  379. WM_UNDO = 0x0304
  380. WM_RENDERFORMAT = 0x0305
  381. WM_RENDERALLFORMATS = 0x0306
  382. WM_DESTROYCLIPBOARD = 0x0307
  383. WM_DRAWCLIPBOARD = 0x0308
  384. WM_PAINTCLIPBOARD = 0x0309
  385. WM_VSCROLLCLIPBOARD = 0x030A
  386. WM_SIZECLIPBOARD = 0x030B
  387. WM_ASKCBFORMATNAME = 0x030C
  388. WM_CHANGECBCHAIN = 0x030D
  389. WM_HSCROLLCLIPBOARD = 0x030E
  390. WM_QUERYNEWPALETTE = 0x030F
  391. WM_PALETTEISCHANGING = 0x0310
  392. WM_PALETTECHANGED = 0x0311
  393. WM_HOTKEY = 0x0312
  394. WM_PRINT = 0x0317
  395. WM_PRINTCLIENT = 0x0318
  396. WM_HANDHELDFIRST = 0x0358
  397. WM_HANDHELDLAST = 0x035F
  398. WM_AFXFIRST = 0x0360
  399. WM_AFXLAST = 0x037F
  400. WM_PENWINFIRST = 0x0380
  401. WM_PENWINLAST = 0x038F
  402. WM_APP = 0x8000
  403. WM_USER = 0x0400
  404. WM_REFLECT = WM_USER + 0x1c00
  405. class WNDCLASSEX(ctypes.Structure):
  406. def __init__(self, *args, **kwargs):
  407. super(WNDCLASSEX, self).__init__(*args, **kwargs)
  408. self.cbSize = ctypes.sizeof(self)
  409. _fields_ = [("cbSize", ctypes.c_uint),
  410. ("style", ctypes.c_uint),
  411. ("lpfnWndProc", WNDPROC),
  412. ("cbClsExtra", ctypes.c_int),
  413. ("cbWndExtra", ctypes.c_int),
  414. ("hInstance", ctypes.wintypes.HANDLE),
  415. ("hIcon", ctypes.wintypes.HANDLE),
  416. ("hCursor", ctypes.wintypes.HANDLE),
  417. ("hBrush", ctypes.wintypes.HANDLE),
  418. ("lpszMenuName", ctypes.wintypes.LPCWSTR),
  419. ("lpszClassName", ctypes.wintypes.LPCWSTR),
  420. ("hIconSm", ctypes.wintypes.HANDLE)]
  421. UpdateWindow = ctypes.windll.user32.UpdateWindow
  422. UpdateWindow.argtypes = [ctypes.wintypes.HWND]
  423. SW_HIDE = 0
  424. SW_SHOWNORMAL = 1
  425. SW_SHOW = 5
  426. ShowWindow = ctypes.windll.user32.ShowWindow
  427. ShowWindow.argtypes = [ctypes.wintypes.HWND, ctypes.c_int]
  428. CS_VREDRAW = 0x0001
  429. CS_HREDRAW = 0x0002
  430. CS_KEYCVTWINDOW = 0x0004
  431. CS_DBLCLKS = 0x0008
  432. CS_OWNDC = 0x0020
  433. CS_CLASSDC = 0x0040
  434. CS_PARENTDC = 0x0080
  435. CS_NOKEYCVT = 0x0100
  436. CS_NOCLOSE = 0x0200
  437. CS_SAVEBITS = 0x0800
  438. CS_BYTEALIGNCLIENT = 0x1000
  439. CS_BYTEALIGNWINDOW = 0x2000
  440. CS_GLOBALCLASS = 0x4000
  441. COLOR_SCROLLBAR = 0
  442. COLOR_BACKGROUND = 1
  443. COLOR_ACTIVECAPTION = 2
  444. COLOR_INACTIVECAPTION = 3
  445. COLOR_MENU = 4
  446. COLOR_WINDOW = 5
  447. COLOR_WINDOWFRAME = 6
  448. COLOR_MENUTEXT = 7
  449. COLOR_WINDOWTEXT = 8
  450. COLOR_CAPTIONTEXT = 9
  451. COLOR_ACTIVEBORDER = 10
  452. COLOR_INACTIVEBORDER = 11
  453. COLOR_APPWORKSPACE = 12
  454. COLOR_HIGHLIGHT = 13
  455. COLOR_HIGHLIGHTTEXT = 14
  456. COLOR_BTNFACE = 15
  457. COLOR_BTNSHADOW = 16
  458. COLOR_GRAYTEXT = 17
  459. COLOR_BTNTEXT = 18
  460. COLOR_INACTIVECAPTIONTEXT = 19
  461. COLOR_BTNHIGHLIGHT = 20
  462. LoadCursor = ctypes.windll.user32.LoadCursorW
  463. def GenerateDummyWindow(callback, uid):
  464. newclass = WNDCLASSEX()
  465. newclass.lpfnWndProc = callback
  466. newclass.style = CS_VREDRAW | CS_HREDRAW
  467. newclass.lpszClassName = uid.replace("-", "")
  468. newclass.hBrush = COLOR_BACKGROUND
  469. newclass.hCursor = LoadCursor(0, 32512)
  470. ATOM = ctypes.windll.user32.RegisterClassExW(ctypes.byref(newclass))
  471. #print "ATOM", ATOM
  472. #print "CLASS", newclass.lpszClassName
  473. hwnd = ctypes.windll.user32.CreateWindowExW(0,
  474. newclass.lpszClassName,
  475. u"Dummy Window",
  476. WS_OVERLAPPEDWINDOW | WS_SYSMENU,
  477. ctypes.windll.user32.GetSystemMetrics(SM_CXVIRTUALSCREEN),
  478. ctypes.windll.user32.GetSystemMetrics(SM_CYVIRTUALSCREEN),
  479. 800, 600, 0, 0, 0, 0)
  480. ShowWindow(hwnd, SW_SHOW)
  481. UpdateWindow(hwnd)
  482. ShowWindow(hwnd, SW_HIDE)
  483. return hwnd
  484. # Message loop calls
  485. TIMERCALLBACK = ctypes.WINFUNCTYPE(None,
  486. ctypes.wintypes.HWND,
  487. ctypes.wintypes.UINT,
  488. ctypes.POINTER(ctypes.wintypes.UINT),
  489. ctypes.wintypes.DWORD)
  490. SetTimer = ctypes.windll.user32.SetTimer
  491. SetTimer.restype = ctypes.POINTER(ctypes.wintypes.UINT)
  492. SetTimer.argtypes = [ctypes.wintypes.HWND,
  493. ctypes.POINTER(ctypes.wintypes.UINT),
  494. ctypes.wintypes.UINT,
  495. TIMERCALLBACK]
  496. KillTimer = ctypes.windll.user32.KillTimer
  497. KillTimer.restype = ctypes.wintypes.BOOL
  498. KillTimer.argtypes = [ctypes.wintypes.HWND,
  499. ctypes.POINTER(ctypes.wintypes.UINT)]
  500. class MSG(ctypes.Structure):
  501. _fields_ = [ ('HWND', ctypes.wintypes.HWND),
  502. ('message', ctypes.wintypes.UINT),
  503. ('wParam', ctypes.wintypes.WPARAM),
  504. ('lParam', ctypes.wintypes.LPARAM),
  505. ('time', ctypes.wintypes.DWORD),
  506. ('pt', POINT)]
  507. GetMessage = ctypes.windll.user32.GetMessageW
  508. GetMessage.restype = ctypes.wintypes.BOOL
  509. GetMessage.argtypes = [ctypes.POINTER(MSG), ctypes.wintypes.HWND, ctypes.wintypes.UINT, ctypes.wintypes.UINT]
  510. TranslateMessage = ctypes.windll.user32.TranslateMessage
  511. TranslateMessage.restype = ctypes.wintypes.ULONG
  512. TranslateMessage.argtypes = [ctypes.POINTER(MSG)]
  513. DispatchMessage = ctypes.windll.user32.DispatchMessageW
  514. DispatchMessage.restype = ctypes.wintypes.ULONG
  515. DispatchMessage.argtypes = [ctypes.POINTER(MSG)]
  516. def LoadIcon(iconfilename, small=False):
  517. return LoadImage(0,
  518. unicode(iconfilename),
  519. IMAGE_ICON,
  520. 16 if small else 0,
  521. 16 if small else 0,
  522. LR_LOADFROMFILE)
  523. class NotificationIcon(object):
  524. def __init__(self, iconfilename, tooltip=None):
  525. assert os.path.isfile(unicode(iconfilename)), "{} doesn't exist".format(iconfilename)
  526. self._iconfile = unicode(iconfilename)
  527. self._hicon = LoadIcon(self._iconfile, True)
  528. assert self._hicon, "Failed to load {}".format(iconfilename)
  529. #self._pumpqueue = Queue.Queue()
  530. self._die = False
  531. self._timerid = None
  532. self._uid = uuid.uuid4()
  533. self._tooltip = unicode(tooltip) if tooltip else u''
  534. #self._thread = threading.Thread(target=self._run)
  535. #self._thread.start()
  536. self._info_bubble = None
  537. self.items = []
  538. def _bubble(self, iconinfo):
  539. if self._info_bubble:
  540. info_bubble = self._info_bubble
  541. self._info_bubble = None
  542. message = unicode(self._info_bubble)
  543. iconinfo.uFlags |= NIF_INFO
  544. iconinfo.szInfo = message
  545. iconinfo.szInfoTitle = message
  546. iconinfo.dwInfoFlags = NIIF_INFO
  547. iconinfo.union.uTimeout = 10000
  548. Shell_NotifyIcon(NIM_MODIFY, ctypes.pointer(iconinfo))
  549. def _run(self):
  550. self._windowproc = WNDPROC(self._callback)
  551. self._hwnd = GenerateDummyWindow(self._windowproc, str(self._uid))
  552. iconinfo = NOTIFYICONDATA()
  553. iconinfo.hWnd = self._hwnd
  554. iconinfo.uID = 100
  555. iconinfo.uFlags = NIF_ICON | NIF_SHOWTIP | NIF_MESSAGE | (NIF_TIP if self._tooltip else 0)
  556. iconinfo.uCallbackMessage = WM_MENUCOMMAND
  557. iconinfo.hIcon = self._hicon
  558. iconinfo.szTip = self._tooltip
  559. iconinfo.dwState = NIS_SHAREDICON
  560. iconinfo.dwInfoFlags = NIIF_INFO
  561. # iconinfo.dwStateMask = NIS_SHAREDICON
  562. iconinfo.szInfo = "Application Title"
  563. iconinfo.union.uTimeout = 5000
  564. Shell_NotifyIcon(NIM_ADD, ctypes.pointer(iconinfo))
  565. iconinfo.union.uVersion = NOTIFYICON_VERSION
  566. Shell_NotifyIcon(NIM_SETVERSION, ctypes.pointer(iconinfo))
  567. self.iconinfo = iconinfo
  568. PostMessage(self._hwnd, WM_NULL, 0, 0)
  569. #self._timerid = SetTimer(self._hwnd, self._timerid, 25, TIMERCALLBACK())
  570. message = MSG()
  571. last_time = -1
  572. ret = None
  573. while not self._die:
  574. try:
  575. ret = GetMessage(ctypes.pointer(message), 0, 0, 0)
  576. TranslateMessage(ctypes.pointer(message))
  577. DispatchMessage(ctypes.pointer(message))
  578. except Exception, err:
  579. # print "NotificationIcon error", err, message
  580. message = MSG()
  581. time.sleep(0.125)
  582. print "Icon thread stopped, removing icon..."
  583. #KillTimer(self._hwnd, self._timerid)
  584. Shell_NotifyIcon(NIM_DELETE, ctypes.cast(ctypes.pointer(iconinfo), ctypes.POINTER(NOTIFYICONDATA)))
  585. ctypes.windll.user32.DestroyWindow(self._hwnd)
  586. ctypes.windll.user32.DestroyIcon(self._hicon)
  587. def _menu(self):
  588. if not hasattr(self, 'items'):
  589. return
  590. menu = CreatePopupMenu()
  591. func = None
  592. try:
  593. iidx = 1000
  594. defaultitem = -1
  595. item_map = {}
  596. for fs in self.items:
  597. iidx += 1
  598. if isinstance(fs, basestring):
  599. if fs and not fs.strip('-_='):
  600. AppendMenu(menu, MF_SEPARATOR, iidx, fs)
  601. else:
  602. AppendMenu(menu, MF_STRING | MF_GRAYED, iidx, fs)
  603. elif isinstance(fs, tuple):
  604. if callable(fs[0]):
  605. itemstring = fs[0]()
  606. else:
  607. itemstring = unicode(fs[0])
  608. flags = MF_STRING
  609. if itemstring.startswith("!"):
  610. itemstring = itemstring[1:]
  611. defaultitem = iidx
  612. if itemstring.startswith("+"):
  613. itemstring = itemstring[1:]
  614. flags = flags | MF_CHECKED
  615. itemcallable = fs[1]
  616. item_map[iidx] = itemcallable
  617. if itemcallable is False:
  618. flags = flags | MF_DISABLED
  619. elif not callable(itemcallable):
  620. flags = flags | MF_GRAYED
  621. AppendMenu(menu, flags, iidx, itemstring)
  622. if defaultitem != -1:
  623. SetMenuDefaultItem(menu, defaultitem, 0)
  624. pos = POINT()
  625. GetCursorPos(ctypes.pointer(pos))
  626. PostMessage(self._hwnd, WM_NULL, 0, 0)
  627. SetForegroundWindow(self._hwnd)
  628. ti = TrackPopupMenu(menu, TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pos.x, pos.y, 0, self._hwnd, None)
  629. if ti in item_map:
  630. func = item_map[ti]
  631. PostMessage(self._hwnd, WM_NULL, 0, 0)
  632. finally:
  633. DestroyMenu(menu)
  634. if func: func()
  635. def clicked(self):
  636. self._menu()
  637. def _callback(self, hWnd, msg, wParam, lParam):
  638. # Check if the main thread is still alive
  639. if msg == WM_TIMER:
  640. if not any(thread.getName() == 'MainThread' and thread.isAlive()
  641. for thread in threading.enumerate()):
  642. self._die = True
  643. elif msg == WM_MENUCOMMAND and lParam == WM_LBUTTONUP:
  644. self.clicked()
  645. elif msg == WM_MENUCOMMAND and lParam == WM_RBUTTONUP:
  646. self._menu()
  647. else:
  648. return DefWindowProc(hWnd, msg, wParam, lParam)
  649. return 1
  650. def die(self):
  651. self._die = True
  652. PostMessage(self._hwnd, WM_NULL, 0, 0)
  653. time.sleep(0.2)
  654. try:
  655. Shell_NotifyIcon(NIM_DELETE, self.iconinfo)
  656. except Exception, err:
  657. print "Icon remove error", err
  658. ctypes.windll.user32.DestroyWindow(self._hwnd)
  659. ctypes.windll.user32.DestroyIcon(self._hicon)
  660. def pump(self):
  661. try:
  662. while not self._pumpqueue.empty():
  663. callable = self._pumpqueue.get(False)
  664. callable()
  665. except Queue.Empty:
  666. pass
  667. def announce(self, text):
  668. self._info_bubble = text
  669. def hideConsole():
  670. ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
  671. def showConsole():
  672. ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 1)
  673. if __name__ == "__main__":
  674. import time
  675. def greet():
  676. ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
  677. print "Hello"
  678. def quit():
  679. ni._die = True
  680. #sys.exit()
  681. def announce():
  682. ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 1)
  683. ni.announce("Hello there")
  684. def clicked():
  685. ni.announce("Hello")
  686. def dynamicTitle():
  687. return "!The time is: %s" % time.time()
  688. ni = NotificationIcon(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../trayicon.ico'), "ZeroNet 0.2.9")
  689. ni.items = [
  690. (dynamicTitle, False),
  691. ('Hello', greet),
  692. ('Title', False),
  693. ('!Default', greet),
  694. ('+Popup bubble', announce),
  695. 'Nothing',
  696. '--',
  697. ('Quit', quit)
  698. ]
  699. ni.clicked = clicked
  700. import atexit
  701. @atexit.register
  702. def goodbye():
  703. print "You are now leaving the Python sector."
  704. ni._run()