/*********************************************************************** Copyright 2002 Ben Rudiak-Gould. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or visit . ***********************************************************************/ #define WIN32_LEAN_AND_MEAN #define STRICT #include #include #include #include #include #include #include "/p/lib/myvector.h" #include "/p/lib/mystring.h" #include "resource.h" #include "../include/dvdsynth-plugin.h" /*******************************************************************\ \*******************************************************************/ union Value { int i; unsigned u; const char* s; }; static inline const char* FormatValue(Value v, char type) { if (type == 's') { return v.s; } else { switch (type) { case 'i': case 'd': case 'u': case 'X': case 'c': { static char buf[3*sizeof(int)]; static char fmt_str[] = "%_"; fmt_str[1] = type; wsprintf(buf, fmt_str, v.i); return buf; } default: return "???"; } } } int Sprint(char* buf, int space, const char* fmt, const char* types, ...) { if (types == 0) { types = "sssssssss"; } int num_types = lstrlen(types); const char* p = fmt; char* q = buf; char* q_end = buf + space - 1; for (;;) { if (*p == '%' && *(p+1) >= '1' && *(p+1) <= '9') { va_list val; va_start(val, types); Value value; int arg_number = *(p+1) - '0'; char type; for (int i=0; i callbacks; Vector menu_stack; HMENU top_level_popup; int base_submenu_level; bool add_separator; bool menu_empty; void InternalAddItem(UINT flags, UINT id, LPCTSTR text); public: PopupMenu() { static DvsMenu_vtable vt = { StaticAddSeparator, StaticAddItem, StaticAddDisabledItem, StaticBeginSubmenu, StaticEndSubmenu, }; vtable = &vt; top_level_popup = CreatePopupMenu(); menu_stack.push_back(top_level_popup); base_submenu_level = 1; add_separator = false; menu_empty = true; } HMENU GetPopup() { return top_level_popup; } void InvokeCallback(int index) { if (index > 0 && index <= callbacks.size()) { callbacks[index-1].Invoke(); } } ~PopupMenu() { DestroyMenu(top_level_popup); } // The purpose of the lock/unlock stuff is to prevent buggy plugins // from screwing up the whole menu structure. void LockSubmenuLevel() { base_submenu_level = menu_stack.size(); } void UnlockSubmenuLevel() { // assert(menu_stack.size() == base_submenu_level); while (menu_stack.size() > base_submenu_level) { EndSubmenu(); } base_submenu_level = 1; } void AddSeparator() { add_separator = true; } void AddItem(const char* text, int checked, void (*callback)(void*, int), void* p, int i) { callbacks.push_back(Callback(callback, p, i)); InternalAddItem(!!checked*MF_CHECKED, callbacks.size(), text); } void AddDisabledItem(const char* text, int checked) { InternalAddItem(MF_GRAYED + !!checked*MF_CHECKED, (UINT)-1, text); } void BeginSubmenu(const char* text, int disabled) { HMENU submenu = CreatePopupMenu(); InternalAddItem(MF_POPUP + !!disabled*MF_GRAYED, (UINT)submenu, text); menu_stack.push_back(submenu); menu_empty = true; } void EndSubmenu() { // assert(menu_stack.size() > base_submenu_level); if (menu_stack.size() > base_submenu_level) { if (menu_empty) { InternalAddItem(MF_GRAYED, 0, "(empty)"); } menu_stack.pop_back(); add_separator = false; //menu_empty = false; } } static void StaticAddSeparator(DvsMenu* self) { reinterpret_cast(self)->AddSeparator(); } static void StaticAddItem(DvsMenu* self, const char* text, int checked, void (*callback)(void*, int), void* p, int i) { reinterpret_cast(self)->AddItem(text, checked, callback, p, i); } static void StaticAddDisabledItem(DvsMenu* self, const char* text, int checked) { reinterpret_cast(self)->AddDisabledItem(text, checked); } static void StaticBeginSubmenu(DvsMenu* self, const char* text, int disabled) { reinterpret_cast(self)->BeginSubmenu(text, disabled); } static void StaticEndSubmenu(DvsMenu* self) { reinterpret_cast(self)->EndSubmenu(); } }; void PopupMenu::InternalAddItem(UINT flags, UINT id, LPCTSTR text) { if (add_separator && !menu_empty) { AppendMenu(menu_stack.back(), MF_SEPARATOR, 0, 0); } AppendMenu(menu_stack.back(), flags, id, text); add_separator = false; menu_empty = false; } /*******************************************************************\ \*******************************************************************/ char g_dvdsynth_directory[MAX_PATH+1]; void ExtractDvdsynthDirectory(const char* exe_name) { char* filepart; if (GetFullPathName(exe_name, MAX_PATH, g_dvdsynth_directory, &filepart)) { if (filepart > g_dvdsynth_directory && (filepart[-1] == '\\' || filepart[-1] == '/')) { filepart[0] = 0; } else { filepart[0] = '\\'; filepart[1] = 0; } } else { lstrcpy(g_dvdsynth_directory, ".\\"); } } // returns 0 on failure, # bytes copied on success const char* GetDvdsynthDirectory() { return g_dvdsynth_directory; } void ChangeToDvdsynthDirectory() { char buf[MAX_PATH]; if (Sprint(buf, MAX_PATH, "%1.", 0, g_dvdsynth_directory) >= 0) { SetCurrentDirectory(buf); } } /*******************************************************************\ \*******************************************************************/ DvsBasePluginFunctions* __cdecl VDevice_DvdsynthBasePluginEntry(DvsBasePluginCallbacks* callbacks); namespace Main { HWND hwnd; bool busy = false; Vector plugins; void* GetTaskbarHWND() { return hwnd; } BOOL WINAPI AboutBoxProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { static HFONT hfont; if (msg == WM_INITDIALOG) { static const char* font_names[] = { "Times New Roman", // "Lucida Sans Unicode", // "Arial Black", }; for (int f = 0; f < sizeof(font_names)/sizeof(font_names[0]); ++f) { hfont = CreateFont(-20, 0, 0, 0, /*FW_BOLD*/ FW_NORMAL, FALSE /*italic*/, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE | DEFAULT_PITCH, font_names[f]); if (hfont) { SendDlgItemMessage(hwnd, IDC_TITLE, WM_SETFONT, (WPARAM)hfont, FALSE); break; } } return TRUE; } else if (msg == WM_COMMAND) { EndDialog(hwnd, 0); return TRUE; } else if (msg == WM_DESTROY) { if (hfont) { DeleteObject(hfont); hfont = 0; } return TRUE; } return FALSE; } void AboutBox(void*, int) { DialogBox(GetModuleHandle(0), MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutBoxProc); } void ExitApp(void*, int) { int i; for (i=0; iQueryExit() != 0) return; } for (i=0; iNotifyExit(); } PostQuitMessage(0); } void HandleTrayMenu() { PopupMenu popup; for (int p=0; pAddMainMenuItems(&popup); popup.UnlockSubmenuLevel(); } popup.AddSeparator(); popup.BeginSubmenu("&Options...", false); { for (int p=0; pAddOptionsMenuItems(&popup); popup.UnlockSubmenuLevel(); } } popup.EndSubmenu(); popup.BeginSubmenu("About...", false); { popup.AddItem("DVDSynth", false, AboutBox, 0, 0); popup.AddSeparator(); for (int p=0; pAddAboutMenuItems(&popup); popup.UnlockSubmenuLevel(); } } popup.EndSubmenu(); popup.AddItem("E&xit", false, ExitApp, 0, 0); POINT pt; GetCursorPos(&pt); SetForegroundWindow(hwnd); int cmd = TrackPopupMenu(popup.GetPopup(), TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL); PostMessage(hwnd, WM_NULL, 0, 0); popup.InvokeCallback(cmd); } typedef DvsBasePluginFunctions* __cdecl DBPE_func(DvsBasePluginCallbacks*); void AddPlugin(DBPE_func* plugin_entry) { static DvsBasePluginCallbacks callbacks = { Sprint, GetDvdsynthDirectory, GetTaskbarHWND }; DvsBasePluginFunctions* plugin_functions = plugin_entry(&callbacks); if (plugin_functions) { plugins.push_back(plugin_functions); } } void LoadPlugins() { // Add internal VDevice plugin AddPlugin(VDevice_DvdsynthBasePluginEntry); // Add external plugins from DLLs in program directory WIN32_FIND_DATA wfd; HANDLE h = FindFirstFile("*.dll", &wfd); if (h != INVALID_HANDLE_VALUE) { do { HMODULE hmod = LoadLibrary(wfd.cFileName); if (hmod != NULL) { DBPE_func* plugin_entry = (DBPE_func*)GetProcAddress(hmod, "DvdsynthBasePluginEntry"); if (plugin_entry) { AddPlugin(plugin_entry); } else { FreeLibrary(hmod); } } } while (FindNextFile(h, &wfd)); FindClose(h); } } BOOL CALLBACK WndEnumProc(HWND hwnd, LPARAM lparam) { if (IsWindowVisible(hwnd)) { *(HWND*)lparam = hwnd; return FALSE; } else { return TRUE; } } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == WM_APP) { if (lparam == WM_LBUTTONDOWN || lparam == WM_RBUTTONDOWN) { if (!busy) { busy = true; Main::HandleTrayMenu(); busy = false; } else { // try to activate the topmost GUI window HWND hwnd = 0; EnumThreadWindows(GetCurrentThreadId(), WndEnumProc, (LPARAM)&hwnd); if (hwnd) { SetForegroundWindow(hwnd); } } } return 0; } else { return DefWindowProc(hwnd, msg, wparam, lparam); } } int main() { static WNDCLASS mywndclass = { 0, WndProc, 0, 0, 0, NULL, NULL, NULL, NULL, "DVDSynth tray window" }; mywndclass.hInstance = GetModuleHandle(0); hwnd = CreateWindow((LPTSTR)RegisterClass(&mywndclass), "", WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, (HINSTANCE)mywndclass.hInstance, NULL); LoadPlugins(); NOTIFYICONDATA nid; #ifdef NOTIFYICONDATA_V1_SIZE nid.cbSize = NOTIFYICONDATA_V1_SIZE; #else nid.cbSize = sizeof(nid); #endif nid.hWnd = hwnd; nid.uID = 0; nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; nid.uCallbackMessage = WM_APP; nid.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_TRAY), IMAGE_ICON, 16, 16, 0); lstrcpy(nid.szTip, "DVDSynth 0.1"); Shell_NotifyIcon(NIM_ADD, &nid); MSG msg; while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } Shell_NotifyIcon(NIM_DELETE, &nid); return msg.wParam; } } /*******************************************************************\ \*******************************************************************/ template class SprintBuf { char buf[n]; SprintBuf(SprintBuf&); public: operator const char*() { return buf; } SprintBuf(const char* fmt, const char* types) { Sprint(buf, n, fmt, types); } template SprintBuf(const char* fmt, const char* types, A a) { Sprint(buf, n, fmt, types, a); } template SprintBuf(const char* fmt, const char* types, A a, B b) { Sprint(buf, n, fmt, types, a, b); } template SprintBuf(const char* fmt, const char* types, A a, B b, C c) { Sprint(buf, n, fmt, types, a, b, c); } template SprintBuf(const char* fmt, const char* types, A a, B b, C c, D d) { Sprint(buf, n, fmt, types, a, b, c, d); } }; /*******************************************************************\ \*******************************************************************/ int main() { ExtractDvdsynthDirectory(__argv[0]); ChangeToDvdsynthDirectory(); InitCommonControls(); // someone's gonna need 'em... return Main::main(); } int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { return main(); }