1
0
mirror of https://github.com/moparisthebest/FireTray synced 2025-01-08 12:08:05 -05:00

* creation of basic icon with text

* load .bmp in addition to .ico (and load them LR_SHARED)
* use ctypes pointer.isNull()
This commit is contained in:
foudfou 2014-02-14 00:22:46 +01:00
parent a10b63a8b2
commit 70f0c0a8e0
6 changed files with 217 additions and 44 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -206,6 +206,7 @@ firetray.Messaging = {
},
updateIcon: function(msgCount) {
log.debug("updateIcon");
if ("undefined" === typeof(msgCount)) msgCount = this.currentMsgCount;
let localizedTooltip;
@ -227,6 +228,7 @@ firetray.Messaging = {
} else if (msgCount > 0) {
let prefMailNotification = firetray.Utils.prefService.getIntPref('mail_notification_type');
log.debug("msgCount prefMailNotification="+prefMailNotification);
switch (prefMailNotification) {
case FIRETRAY_NOTIFICATION_MESSAGE_COUNT:
let prefIconTextColor = firetray.Utils.prefService.getCharPref("icon_text_color");

View File

@ -0,0 +1,93 @@
var EXPORTED_SYMBOLS = [ "gdi32" ];
const GDI32_LIBNAME = "gdi32";
const GDI32_ABIS = [ "dll" ];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
function gdi32_defines(lib) {
this.BITMAP = ctypes.StructType("BITMAP", [
{ "bmType": win32.LONG },
{ "bmWidth": win32.LONG },
{ "bmHeight": win32.LONG },
{ "bmWidthBytes": win32.LONG },
{ "bmPlanes": win32.WORD },
{ "bmBitsPixel": win32.WORD },
{ "bmBits": win32.LPVOID }
]);
this.PBITMAP = this.BITMAP.ptr;
lib.lazy_bind("CreateCompatibleDC", win32.HDC, win32.HDC);
lib.lazy_bind("DeleteDC", win32.BOOL, win32.HDC);
lib.lazy_bind("CreateCompatibleBitmap", win32.HBITMAP, win32.HDC, ctypes.int, ctypes.int);
lib.lazy_bind("SelectObject", win32.HGDIOBJ, win32.HDC, win32.HGDIOBJ);
lib.lazy_bind("DeleteObject", win32.BOOL, win32.HGDIOBJ);
lib.lazy_bind("PatBlt", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.DWORD);
this.BLACKNESS = win32.DWORD(0x00000042); /* dest = BLACK */
this.WHITENESS = win32.DWORD(0x00FF0062); /* dest = WHITE */
lib.lazy_bind("CreateFontW", win32.HFONT, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.LPCWSTR);
this.FW_DONTCARE = 0;
this.FW_THIN = 100;
this.FW_EXTRALIGHT = 200;
this.FW_LIGHT = 300;
this.FW_NORMAL = 400;
this.FW_MEDIUM = 500;
this.FW_SEMIBOLD = 600;
this.FW_BOLD = 700;
this.FW_EXTRABOLD = 800;
this.FW_HEAVY = 900;
this.FF_DONTCARE = (0<<4); /* Don't care or don't know. */
this.FF_ROMAN = (1<<4); /* Variable stroke width, serifed. Times Roman, Century Schoolbook, etc. */
this.FF_SWISS = (2<<4); /* Variable stroke width, sans-serifed. Helvetica, Swiss, etc. */
this.FF_MODERN = (3<<4); /* Constant stroke width, serifed or sans-serifed. Pica, Elite, Courier, etc. */
this.FF_SCRIPT = (4<<4); /* Cursive, etc. */
this.FF_DECORATIVE = (5<<4); /* Old English, etc. */
this.DEFAULT_PITCH = 0;
this.FIXED_PITCH = 1;
this.VARIABLE_PITCH = 2;
this.MONO_FONT = 8;
this.ANSI_CHARSET = 0;
this.DEFAULT_CHARSET = 1;
this.SYMBOL_CHARSET = 2;
this.SHIFTJIS_CHARSET = 128;
this.HANGEUL_CHARSET = 129;
this.HANGUL_CHARSET = 129;
this.GB2312_CHARSET = 134;
this.CHINESEBIG5_CHARSET = 136;
this.OEM_CHARSET = 255;
this.JOHAB_CHARSET = 130;
this.HEBREW_CHARSET = 177;
this.ARABIC_CHARSET = 178;
this.GREEK_CHARSET = 161;
this.TURKISH_CHARSET = 162;
this.VIETNAMESE_CHARSET = 163;
this.THAI_CHARSET = 222;
this.EASTEUROPE_CHARSET = 238;
this.RUSSIAN_CHARSET = 204;
lib.lazy_bind("SetTextColor", win32.COLORREF, win32.HDC, win32.COLORREF);
lib.lazy_bind("SetBkMode", ctypes.int, win32.HDC, ctypes.int);
this.TRANSPARENT = 1;
this.OPAQUE = 2;
this.BKMODE_LAST = 2;
lib.lazy_bind("TextOutW", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, win32.LPCTSTR, ctypes.int);
lib.lazy_bind("GetTextAlign", win32.UINT, win32.HDC);
lib.lazy_bind("SetTextAlign", win32.UINT, win32.HDC, win32.UINT);
this.TA_LEFT = 0;
this.TA_RIGHT = 2;
this.TA_CENTER = 6;
this.TA_TOP = 0;
this.TA_BOTTOM = 8;
this.TA_BASELINE = 24;
this.TA_RTLREADING = 256;
this.TA_MASK =(this.TA_BASELINE+this.TA_CENTER+this.TA_UPDATECP+this.TA_RTLREADING);
}
new ctypes_library(GDI32_LIBNAME, GDI32_ABIS, gdi32_defines, this);

View File

@ -51,6 +51,9 @@ function user32_defines(lib) {
this.IDI_EXCLAMATION = win32.MAKEINTRESOURCE(32515);
this.IDI_ASTERISK = win32.MAKEINTRESOURCE(32516);
lib.lazy_bind("LoadImageW", win32.HANDLE, win32.HINSTANCE, win32.LPCTSTR, win32.UINT, ctypes.int, ctypes.int, win32.UINT);
this.IMAGE_BITMAP = 0;
this.IMAGE_ICON = 1;
this.IMAGE_CURSOR = 2;
this.LR_CREATEDIBSECTION = 0x00002000;
this.LR_DEFAULTCOLOR = 0x00000000;
this.LR_DEFAULTSIZE = 0x00000040;
@ -241,6 +244,10 @@ function user32_defines(lib) {
this.SPI_GETFOREGROUNDFLASHCOUNT = 0x2004;
lib.lazy_bind("GetForegroundWindow", win32.HWND);
lib.lazy_bind("GetDC", win32.HDC, win32.HWND);
lib.lazy_bind("ReleaseDC", ctypes.int, win32.HWND, win32.HDC);
lib.lazy_bind("CreateIconIndirect", win32.HICON, win32.PICONINFO);
}
new ctypes_library(USER32_LIBNAME, USER32_ABIS, user32_defines, this);

View File

@ -41,6 +41,10 @@ var win32 = new function() {
this.HBRUSH = this.HICON;
this.HCURSOR = this.HANDLE;
this.HHOOK = this.HANDLE;
this.HDC = this.HANDLE;
this.HGDIOBJ = this.HANDLE;
this.HBITMAP = this.HANDLE;
this.HFONT = this.HANDLE;
this.TCHAR = ctypes.jschar, // Mozilla compiled with UNICODE/_UNICODE macros and wchar_t = jschar
this.LPSTR = ctypes.char.ptr;
this.LPCSTR = ctypes.char.ptr;
@ -52,6 +56,7 @@ var win32 = new function() {
this.WPARAM = this.UINT_PTR;
this.LPARAM = this.LONG_PTR;
this.FARPROC = ctypes.voidptr_t; // typedef INT_PTR (FAR WINAPI *FARPROC)();
this.COLORREF = this.DWORD; // 0x00bbggrr
this.GUID = ctypes.StructType("GUID", [
{ "Data1": ctypes.unsigned_long },
@ -97,10 +102,18 @@ var win32 = new function() {
this.WM_MOUSELAST = 0x020D;
this.WM_MOUSELAST = 0x020A;
this.ICONINFO = ctypes.StructType("ICONINFO", [
{ "fIcon": this.BOOL },
{ "xHotspot": this.DWORD },
{ "yHotspot": this.DWORD },
{ "hbmMask": this.HBITMAP },
{ "hbmColor": this.HBITMAP }
]);
this.PICONINFO = this.ICONINFO.ptr;
};
// ShellAPI.h
let nin_select = win32.WM_USER + 0;
win32.NIN_SELECT = nin_select;
win32.NINF_KEY = 0x1;
win32.NIN_SELECT = nin_select;
win32.NINF_KEY = 0x1;
win32.NIN_KEYSELECT = (win32.NIN_SELECT | win32.NINF_KEY);

View File

@ -14,6 +14,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypesMap.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
Cu.import("resource://firetray/ctypes/winnt/gdi32.jsm");
Cu.import("resource://firetray/ctypes/winnt/kernel32.jsm");
Cu.import("resource://firetray/ctypes/winnt/shell32.jsm");
Cu.import("resource://firetray/ctypes/winnt/user32.jsm");
@ -27,6 +28,7 @@ if ("undefined" == typeof(firetray.Handler))
log.error("This module MUST be imported from/after FiretrayHandler !");
FIRETRAY_ICON_CHROME_PATHS = {
'blank-icon': "chrome://firetray/skin/winnt/blank-icon.bmp",
'mail-unread': "chrome://firetray/skin/winnt/mail-unread.ico",
};
@ -36,11 +38,12 @@ firetray.StatusIcon = {
notifyIconData: null,
hwndProxy: null,
icons: null,
bitmaps: null,
WNDCLASS_NAME: "FireTrayHiddenWindowClass",
WNDCLASS_ATOM: null,
init: function() {
this.loadIcons();
this.loadImages();
// this.defineIconNames(); // FIXME: linux-only
this.create();
@ -52,7 +55,7 @@ firetray.StatusIcon = {
log.debug("Disabling StatusIcon");
this.destroy();
this.destroyIcons();
this.destroyImages();
this.initialized = false;
return true;
@ -74,8 +77,9 @@ firetray.StatusIcon = {
this.defaultNewMailIconName = "mail-unread";
},
loadIcons: function() {
loadImages: function() {
this.icons = new ctypesMap(win32.HICON);
this.bitmaps = new ctypesMap(win32.HBITMAP);
// the Mozilla hidden window has the default Mozilla icon
let hwnd_hidden_moz = user32.FindWindowW("MozillaHiddenWindowClass", null);
@ -84,39 +88,39 @@ firetray.StatusIcon = {
/* we'll take the first icon in the .ico file. To get the icon count in the
file, pass ctypes.cast(ctypes.int(-1), win32.UINT); */
for (let ico_name in FIRETRAY_ICON_CHROME_PATHS) {
let path = firetray.Utils.chromeToPath(FIRETRAY_ICON_CHROME_PATHS[ico_name]);
let hicon = shell32.ExtractIconW(null, path, 0);
// ERROR_INVALID_HANDLE(6) ignored (_Reserved_ HINSTANCE hInst ?)
this.icons.insert(ico_name, hicon);
log.debug("icon '"+ico_name+"'="+this.icons.get(ico_name)+" winLastError="+ctypes.winLastError);
for (let imgName in FIRETRAY_ICON_CHROME_PATHS) {
let path = firetray.Utils.chromeToPath(FIRETRAY_ICON_CHROME_PATHS[imgName]);
let imgType = path.substr(-3, 3);
let imgTypeDict = {
ico: { win_t: win32.HICON, load_const: user32.IMAGE_ICON, map: this.icons },
bmp: { win_t: win32.HBITMAP, load_const: user32.IMAGE_BITMAP, map: this.bitmaps }
};
if (!(imgType in imgTypeDict)) {
throw Error("Unrecognized type '"+imgType+"'");
}
let imgTypeRec = imgTypeDict[imgType];
let himg = ctypes.cast(
user32.LoadImageW(null, path, imgTypeRec['load_const'], 0, 0,
user32.LR_LOADFROMFILE|user32.LR_SHARED),
imgTypeRec['win_t']);
if (himg.isNull()) {
log.error("Could not load '"+imgName+"'="+himg+" winLastError="+ctypes.winLastError);
continue;
}
imgTypeRec['map'].insert(imgName, himg);
}
},
destroyIcons: function() {
let success = true, errors = [];
let keys = this.icons.keys;
// 'app' must not get DestroyIcon'd
var idx_app = keys.indexOf('app');
if (idx_app > -1) keys.splice(idx_app, 1);
for (let i=0, len=keys.length; i<len; ++i) {
let ico_name = keys[i];
let res = user32.DestroyIcon(this.icons.get(ico_name));
if (res)
this.icons.remove(ico_name);
else
errors.push(ctypes.winLastError);
success = success && res;
}
this.icons.remove('app');
if (!success) {
log.error("Couldn't destroy all icons: "+errors);
} else {
log.debug("Icons destroyed");
}
// images loaded with LR_SHARED need't be destroyed
destroyImages: function() {
[this.icons, this.bitmaps].forEach(function(map, idx, ary) {
let keys = map.keys;
for (let i=0, len=keys.length; i<len; ++i) {
let imgName = keys[i];
map.remove(imgName);
}
});
log.debug("Icons destroyed");
},
create: function() {
@ -190,7 +194,9 @@ firetray.StatusIcon = {
break;
case win32.WM_RBUTTONUP:
log.debug("WM_RBUTTONUP");
firetray.Handler.windowGetAttention(); // TESTING
let hicon = firetray.StatusIcon.createSmallIcon(hWnd, "100", "#990000");
log.debug("%%%%%%%%%% ICON="+hicon);
firetray.StatusIcon.setImageFromIcon(hicon);
break;
case win32.WM_CONTEXTMENU:
log.debug("WM_CONTEXTMENU");
@ -212,17 +218,16 @@ firetray.StatusIcon = {
// result is a ctypes.Int64. So we need to create a CData from it before
// casting it to a HICON.
let icon = ctypes.cast(win32.LRESULT(rv), win32.HICON);
let NULL = win32.HICON(null); // for comparison only
if (firetray.js.strEquals(icon, NULL)) { // from the window class
if (icon.isNull()) { // from the window class
rv = user32.GetClassLong(hwnd, user32.GCLP_HICONSM);
icon = ctypes.cast(win32.ULONG_PTR(rv), win32.HICON);
log.debug("GetClassLong winLastError="+ctypes.winLastError);
}
if (firetray.js.strEquals(icon, NULL)) { // from the first resource -> ERROR_RESOURCE_TYPE_NOT_FOUND(1813)
if (icon.isNull()) { // from the first resource -> ERROR_RESOURCE_TYPE_NOT_FOUND(1813)
icon = user32.LoadIconW(firetray.Win32.hInstance, win32.MAKEINTRESOURCE(0));
log.debug("LoadIconW module winLastError="+ctypes.winLastError);
}
if (firetray.js.strEquals(icon, NULL)) { // OS default icon
if (icon.isNull()) { // OS default icon
icon = user32.LoadIconW(null, win32.MAKEINTRESOURCE(user32.IDI_APPLICATION));
log.debug("LoadIconW default winLastError="+ctypes.winLastError);
}
@ -247,22 +252,75 @@ firetray.StatusIcon = {
this.destroyProxyWindow();
},
setImageFromIcon: function(icoName) {
setImageFromIcon: function(hicon) {
let nid = firetray.StatusIcon.notifyIconData;
nid.hIcon = firetray.StatusIcon.icons.get(icoName);
nid.hIcon = hicon;
rv = shell32.Shell_NotifyIconW(shell32.NIM_MODIFY, nid.address());
log.debug("Shell_NotifyIcon MODIFY="+rv+" winLastError="+ctypes.winLastError);
},
// rgb colors encoded in *bbggrr*
cssColorToCOLORREF: function(csscolor) {
let rgb = csscolor.substr(1);
let rr = rgb.substr(0, 2);
let gg = rgb.substr(2, 2);
let bb = rgb.substr(4, 2);
return parseInt("0x"+bb+gg+rr);
},
// http://stackoverflow.com/questions/457050/how-to-display-text-in-system-tray-icon-with-win32-api
createSmallIcon: function(hWnd, text, color) {
let hdc = user32.GetDC(hWnd); // get device context (DC) for hWnd
let hdcMem = gdi32.CreateCompatibleDC(hdc); // creates a memory device context (DC) compatible with hdc (need a bitmap)
let hBitmap = this.bitmaps.get('blank-icon');
let hBitmapMask = gdi32.CreateCompatibleBitmap(hdc, 32, 32);
user32.ReleaseDC(hWnd, hdc);
let hOldBitmap = ctypes.cast(gdi32.SelectObject(hdcMem, hBitmap), // replace bitmap in hdcMem by hBitmap
win32.HBITMAP);
// gdi32.PatBlt(hdcMem, 0, 0, 16, 16, gdi32.BLACKNESS); // paint black rectangle
let nHeight = 32, fnWeight = gdi32.FW_BOLD;
let hFont = gdi32.CreateFontW(nHeight, 0, 0, 0, fnWeight, 0, 0, 0,
gdi32.ANSI_CHARSET, 0, 0, 0, gdi32.FF_SWISS, "Sans"); // get font
hFont = ctypes.cast(gdi32.SelectObject(hdcMem, hFont), win32.HFONT); // replace font in bitmap by hFont
gdi32.SetTextColor(hdcMem, win32.COLORREF(this.cssColorToCOLORREF(color)));
// gdi32.SetBkMode(hdcMem, gdi32.TRANSPARENT); // VERY IMPORTANT
// gdi32.SetTextAlign(hdcMem, gdi32.GetTextAlign(hdcMem) & (~gdi32.TA_CENTER));
// gdi32.SetTextAlign(hdcMem, gdi32.TA_CENTER);
log.debug(" ___ALIGN=(winLastError="+ctypes.winLastError+") "+gdi32.GetTextAlign(hdcMem));
gdi32.TextOutW(hdcMem, 0, 0, text, text.length);
gdi32.SelectObject(hdcMem, hOldBitmap); // always replace new hBitmap with original one
let iconInfo = win32.ICONINFO();
iconInfo.fIcon = true;
iconInfo.xHotspot = 0;
iconInfo.yHotspot = 0;
iconInfo.hbmMask = hBitmapMask;
iconInfo.hbmColor = hBitmap;
let hIcon = user32.CreateIconIndirect(iconInfo.address());
gdi32.DeleteObject(gdi32.SelectObject(hdcMem, hFont));
gdi32.DeleteDC(hdcMem);
// gdi32.DeleteDC(hdc); // already ReleaseDC's
gdi32.DeleteObject(hBitmap);
gdi32.DeleteObject(hBitmapMask);
return hIcon; // to be destroyed (DestroyIcon)
}
}; // firetray.StatusIcon
firetray.Handler.setIconImageDefault = function() {
log.debug("setIconImageDefault");
firetray.StatusIcon.setImageFromIcon('app');
firetray.StatusIcon.setImageFromIcon(firetray.StatusIcon.icons.get('app'));
};
firetray.Handler.setIconImageNewMail = function() {
firetray.StatusIcon.setImageFromIcon('mail-unread');
log.debug("setIconImageDefault");
firetray.StatusIcon.setImageFromIcon(firetray.StatusIcon.icons.get('mail-unread'));
};
// firetray.Handler.setIconImageFromFile = firetray.StatusIcon.setIconImageFromFile;