From 7f08580a244f2a37f6550ddbc751a4a17ded1534 Mon Sep 17 00:00:00 2001 From: Ron Pedde Date: Mon, 29 May 2006 09:14:04 +0000 Subject: [PATCH] Merge from dev-FireflyShell branch --- src/plugins/w32-event.c | 45 +++ win32/FireflyShell/AboutPage.cpp | 133 +++++++++ win32/FireflyShell/AboutPage.h | 72 +++++ win32/FireflyShell/AdvancedPage.cpp | 167 +++++++++++ win32/FireflyShell/AdvancedPage.h | 98 +++++++ win32/FireflyShell/ConfigPage.cpp | 90 ++++++ win32/FireflyShell/ConfigPage.h | 70 +++++ win32/FireflyShell/DosPath.cpp | 173 +++++++++++ win32/FireflyShell/DosPath.h | 51 ++++ win32/FireflyShell/FireflyShell.cpp | 373 ++++++++++++++++++++++++ win32/FireflyShell/FireflyShell.h | 154 ++++++++++ win32/FireflyShell/FireflyShell.rc | 301 +++++++++++++++++++ win32/FireflyShell/FireflyShell.vcproj | 370 +++++++++++++++++++++++ win32/FireflyShell/IniFile.h | 68 +++++ win32/FireflyShell/LogPage.cpp | 23 ++ win32/FireflyShell/LogPage.h | 41 +++ win32/FireflyShell/MainDlg.cpp | 59 ++++ win32/FireflyShell/MainDlg.h | 47 +++ win32/FireflyShell/NotifyIcon.cpp | 270 +++++++++++++++++ win32/FireflyShell/NotifyIcon.h | 81 +++++ win32/FireflyShell/README.txt | 17 ++ win32/FireflyShell/ServerEvents.cpp | 142 +++++++++ win32/FireflyShell/ServerEvents.h | 55 ++++ win32/FireflyShell/ServiceControl.cpp | 205 +++++++++++++ win32/FireflyShell/ServiceControl.h | 171 +++++++++++ win32/FireflyShell/VersionInfo.cpp | 111 +++++++ win32/FireflyShell/VersionInfo.h | 46 +++ win32/FireflyShell/res/FireflyShell.ico | Bin 0 -> 1150 bytes win32/FireflyShell/res/ShellRunning.ico | Bin 0 -> 1150 bytes win32/FireflyShell/res/ShellStopped.ico | Bin 0 -> 1150 bytes win32/FireflyShell/resource.h | 59 ++++ win32/FireflyShell/singleton.h | 49 ++++ win32/FireflyShell/stdafx.cpp | 21 ++ win32/FireflyShell/stdafx.h | 77 +++++ win32/FireflyShell/version.h.templ | 13 + win32/FireflyShell/version.rc | 44 +++ win32/versionize.bat | 2 + 37 files changed, 3698 insertions(+) create mode 100644 win32/FireflyShell/AboutPage.cpp create mode 100644 win32/FireflyShell/AboutPage.h create mode 100644 win32/FireflyShell/AdvancedPage.cpp create mode 100644 win32/FireflyShell/AdvancedPage.h create mode 100644 win32/FireflyShell/ConfigPage.cpp create mode 100644 win32/FireflyShell/ConfigPage.h create mode 100644 win32/FireflyShell/DosPath.cpp create mode 100644 win32/FireflyShell/DosPath.h create mode 100644 win32/FireflyShell/FireflyShell.cpp create mode 100644 win32/FireflyShell/FireflyShell.h create mode 100644 win32/FireflyShell/FireflyShell.rc create mode 100644 win32/FireflyShell/FireflyShell.vcproj create mode 100644 win32/FireflyShell/IniFile.h create mode 100644 win32/FireflyShell/LogPage.cpp create mode 100644 win32/FireflyShell/LogPage.h create mode 100644 win32/FireflyShell/MainDlg.cpp create mode 100644 win32/FireflyShell/MainDlg.h create mode 100644 win32/FireflyShell/NotifyIcon.cpp create mode 100644 win32/FireflyShell/NotifyIcon.h create mode 100644 win32/FireflyShell/README.txt create mode 100644 win32/FireflyShell/ServerEvents.cpp create mode 100644 win32/FireflyShell/ServerEvents.h create mode 100644 win32/FireflyShell/ServiceControl.cpp create mode 100644 win32/FireflyShell/ServiceControl.h create mode 100644 win32/FireflyShell/VersionInfo.cpp create mode 100644 win32/FireflyShell/VersionInfo.h create mode 100644 win32/FireflyShell/res/FireflyShell.ico create mode 100644 win32/FireflyShell/res/ShellRunning.ico create mode 100644 win32/FireflyShell/res/ShellStopped.ico create mode 100644 win32/FireflyShell/resource.h create mode 100644 win32/FireflyShell/singleton.h create mode 100644 win32/FireflyShell/stdafx.cpp create mode 100644 win32/FireflyShell/stdafx.h create mode 100644 win32/FireflyShell/version.h.templ create mode 100644 win32/FireflyShell/version.rc diff --git a/src/plugins/w32-event.c b/src/plugins/w32-event.c index 214406df..59469749 100644 --- a/src/plugins/w32-event.c +++ b/src/plugins/w32-event.c @@ -11,6 +11,9 @@ void plugin_handler(int, int, void *, int); #define PIPE_BUFFER_SIZE 4096 +#define USE_UDP 0 +#define USE_MAILSLOT 1 + /* Globals */ PLUGIN_EVENT_FN _pefn = { plugin_handler }; PLUGIN_INPUT_FN *_ppi; @@ -40,6 +43,7 @@ PLUGIN_INFO *plugin_info(PLUGIN_INPUT_FN *ppi) { return &_pi; } +#if USE_UDP /** NO LOG IN HERE! We'll go into an endless loop. :) */ void plugin_handler(int event_id, int intval, void *vp, int len) { int total_len = 3 * sizeof(int) + len + 1; @@ -76,3 +80,44 @@ void plugin_handler(int event_id, int intval, void *vp, int len) { free(pmsg); return; } +#endif /* USE_UDP */ + +#if USE_MAILSLOT +#define MAILSLOT_NAME "\\\\.\\mailslot\\FireflyMediaServer--67A72768-4154-417e-BFA0-FA9B50C342DE" +/** NO LOG IN HERE! We'll go into an endless loop. :) */ +void plugin_handler(int event_id, int intval, void *vp, int len) { + HANDLE h = CreateFile(MAILSLOT_NAME, GENERIC_WRITE, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h != INVALID_HANDLE_VALUE) + { + DWORD bytes_written; + const int packet_size = 12 + len; + unsigned char *buffer = (unsigned char *)malloc(packet_size); + if(!buffer) + return; + memset(buffer, 0, packet_size); + + buffer[0] = packet_size & 0xff; + buffer[1] = (packet_size >> 8) & 0xff; + buffer[2] = (packet_size >> 16) & 0xff; + buffer[3] = (packet_size >> 24) & 0xff; + + buffer[4] = event_id & 0xff; + buffer[5] = (event_id >> 8) & 0xff; + buffer[6] = (event_id >> 16) & 0xff; + buffer[7] = (event_id >> 24) & 0xff; + + buffer[8] = intval & 0xff; + buffer[9] = (intval >> 8) & 0xff; + buffer[10] = (intval >> 16) & 0xff; + buffer[11] = (intval >> 24) & 0xff; + + memcpy(buffer + 12, vp, len); + + /* If this fails then there's nothing we can do about it anyway. */ + WriteFile(h, buffer, packet_size, &bytes_written, NULL); + CloseHandle(h); + } +} +#endif /* USE_MAILSLOT */ diff --git a/win32/FireflyShell/AboutPage.cpp b/win32/FireflyShell/AboutPage.cpp new file mode 100644 index 00000000..c5593601 --- /dev/null +++ b/win32/FireflyShell/AboutPage.cpp @@ -0,0 +1,133 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "AboutPage.h" +#include "FireflyShell.h" +#include "VersionInfo.h" +#include "DosPath.h" + +LRESULT CAboutPage::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) +{ + // Do this before we try and use the controls. + DoDataExchange(false); + + FillVersionList(); + + return 0; +} + +void CAboutPage::FillVersionList() +{ + m_versions.Empty(); + + // Initialise the list control + CString str; + str.LoadString(IDS_VERSIONINFO_DESCRIPTION); + m_list.AddColumn(str, SUBITEM_DESCRIPTION); + str.LoadString(IDS_VERSIONINFO_VERSION); + m_list.AddColumn(str, SUBITEM_VERSION, 1); + str.LoadString(IDS_VERSIONINFO_PATH); + m_list.AddColumn(str, SUBITEM_PATH, 2); + + m_list.SetColumnWidth(SUBITEM_DESCRIPTION, 40); + m_list.SetColumnWidth(SUBITEM_VERSION, 40); + + CDosPath server_path(GetApplication()->GetServiceBinaryPath()); + AddEntry(server_path.GetPath(), _T("Firefly server")); + AddEntry(CDosPath::AppPath().GetPath(), _T("FireflyShell")); + + CString plugins_path = server_path.GetPathOnly() + _T("plugins\\"); + CString plugins_pattern = plugins_path + _T("*.dll"); + WIN32_FIND_DATA find; + HANDLE hFind = FindFirstFile(plugins_pattern, &find); + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + AddEntry(plugins_path + find.cFileName, CString(find.cFileName) + _T(" plugin")); + } while (FindNextFile(hFind, &find)); + FindClose(hFind); + } + + for(int i = 0; i < SUBITEM_COUNT; ++i) + { + m_list.SetColumnWidth(i, m_column_widths[i] + 16); + } +} + +void CAboutPage::AddEntry(const TCHAR *path, const TCHAR *fallback_description) +{ + VersionInfo vi; + + CString description = fallback_description; + CString version; + + if (vi.Open(path)) + { + description = vi.GetFileDescription(); + version = vi.GetFileVersion(); + } + + int item = m_list.GetItemCount(); + AddItem(item, SUBITEM_DESCRIPTION, description); + AddItem(item, SUBITEM_VERSION, version); + AddItem(item, SUBITEM_PATH, path); + + CString line; + line.Format(_T("%s\t%s\t%s\r\n"), description, version, path); + m_versions += line; +} + +void CAboutPage::AddItem(int item, int subitem, const TCHAR *text) +{ + m_list.AddItem(item, subitem, text); + const int width = m_list.GetStringWidth(text); + if (width > m_column_widths[subitem]) + m_column_widths[subitem] = width; +} + +LRESULT CAboutPage::OnWebsite(WORD, WORD, HWND, BOOL &) +{ + const TCHAR *url = _T("http://www.fireflymediaserver.org/"); + ::ShellExecute(m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWNORMAL); + return 0; +} + +LRESULT CAboutPage::OnCopy(WORD, WORD, HWND, BOOL &) +{ + if (OpenClipboard()) + { + const size_t len = m_versions.GetLength() * sizeof(TCHAR); + + HGLOBAL h = ::GlobalAlloc(GMEM_MOVEABLE, len); + if (h) + { + void *buffer = ::GlobalLock(h); + memcpy(buffer, static_cast(m_versions), len); + ::GlobalUnlock(h); + + EmptyClipboard(); +#if defined(UNICODE) + SetClipboardData(CF_UNICODETEXT, h); +#else + SetClipboardData(CF_TEXT, h); +#endif + } + CloseClipboard(); + } + return 0; +} \ No newline at end of file diff --git a/win32/FireflyShell/AboutPage.h b/win32/FireflyShell/AboutPage.h new file mode 100644 index 00000000..6938ee2f --- /dev/null +++ b/win32/FireflyShell/AboutPage.h @@ -0,0 +1,72 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef ABOUTPAGE_H +#define ABOUTPAGE_H 1 + +#include "resource.h" + +class CAboutPage : + public CPropertyPageImpl, + public CWinDataExchange +{ + typedef CPropertyPageImpl base; + CListViewCtrl m_list; + enum + { + SUBITEM_DESCRIPTION = 0, + SUBITEM_VERSION = 1, + SUBITEM_PATH = 2, + SUBITEM_COUNT = 3 + }; + + int m_column_widths[SUBITEM_COUNT]; + + // String version of information ready to write to the clipboard + CString m_versions; + +public: + CAboutPage() + { + ::ZeroMemory(m_column_widths, sizeof(m_column_widths)); + } + enum { IDD = IDD_PAGE_ABOUT }; + +private: + + // Message Handlers + BEGIN_MSG_MAP(CAboutPage) + MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) + COMMAND_ID_HANDLER(IDC_WEBSITE, OnWebsite) + COMMAND_ID_HANDLER(IDC_COPY, OnCopy) + CHAIN_MSG_MAP(base) + END_MSG_MAP() + + BEGIN_DDX_MAP(CAboutPage) + DDX_CONTROL_HANDLE(IDC_VERSIONLIST, m_list) + END_DDX_MAP() + + LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnWebsite(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + LRESULT OnCopy(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + + void FillVersionList(); + void AddEntry(const TCHAR *path, const TCHAR *fallback_description); + + void AddItem(int item, int subitem, const TCHAR *text); +}; + +#endif // ABOUTPAGE_H \ No newline at end of file diff --git a/win32/FireflyShell/AdvancedPage.cpp b/win32/FireflyShell/AdvancedPage.cpp new file mode 100644 index 00000000..31d691f4 --- /dev/null +++ b/win32/FireflyShell/AdvancedPage.cpp @@ -0,0 +1,167 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "AdvancedPage.h" +#include "IniFile.h" +#include "FireflyShell.h" + +LRESULT CAdvancedPage::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) +{ + IniFile ini(GetApplication()->GetConfigPath()); + m_server_port = ini.GetInteger(_T("general"), _T("port"), 9999); + + DoDataExchange(false); + m_port_spin.SetRange32(1, 65535); + + switch (GetApplication()->IsAutoStartEnabled()) + { + case 0: + m_autostart_check.SetCheck(BST_UNCHECKED); + break; + case 1: + m_autostart_check.SetCheck(BST_CHECKED); + break; + case 2: + // Be sneaky here. Make it capable of showing indeterminate but + // don't make it automatically change on click. We'll revert + // to a normal checkbox when the click happens. + m_autostart_check.SetButtonStyle(BS_3STATE); + m_autostart_check.SetCheck(BST_INDETERMINATE); + break; + } + UpdateControls(); + + GetApplication()->ServiceStatusSubscribe(this); + return 0; +} + +void CAdvancedPage::OnDestroy() +{ + GetApplication()->ServiceStatusUnsubscribe(this); +} + +void CAdvancedPage::UpdateControls() +{ + Service::Status status = GetApplication()->GetServiceStatus(); + UpdateControls(status); +} + +void CAdvancedPage::UpdateControls(Service::Status status) +{ + UINT state_id; + if (status.IsPending()) + { + state_id = IDS_SERVER_PENDING; + GetDlgItem(IDC_STARTSERVICE).ShowWindow(SW_HIDE); + GetDlgItem(IDC_STOPSERVICE).ShowWindow(SW_HIDE); + } + else if (status.IsRunning()) + { + state_id = IDS_SERVER_RUNNING; + GetDlgItem(IDC_STARTSERVICE).ShowWindow(SW_HIDE); + GetDlgItem(IDC_STOPSERVICE).ShowWindow(SW_SHOW); + } + else + { + state_id = IDS_SERVER_STOPPED; + GetDlgItem(IDC_STARTSERVICE).ShowWindow(SW_SHOW); + GetDlgItem(IDC_STOPSERVICE).ShowWindow(SW_HIDE); + } + + const bool can_configure = GetApplication()->CanConfigure(); + GetDlgItem(IDC_SERVERPORT).EnableWindow(can_configure); + GetDlgItem(IDC_PORTSPIN).EnableWindow(can_configure); + + // If we can't control the service then don't give the user + // the impression that we can. + const bool can_control = GetApplication()->CanControlService(); + GetDlgItem(IDC_STARTSERVICE).EnableWindow(can_control); + GetDlgItem(IDC_STOPSERVICE).EnableWindow(can_control); + GetDlgItem(IDC_AUTOSTART).EnableWindow(can_control); + + CString state; + state.LoadString(state_id); + if (!can_control) + { + CString s; + s.LoadString(IDS_NOT_ADMIN); + state += " "; + state += s; + } + + GetDlgItem(IDC_SERVERSTATE).SetWindowText(state); +} + +int CAdvancedPage::OnApply() +{ + ATLTRACE("CAdvancedPage::OnApply\n"); + + if (!DoDataExchange(true)) + return false; + + IniFile ini(GetApplication()->GetConfigPath()); + ini.SetInteger(_T("general"), _T("port"), m_server_port); + + switch (m_autostart_check.GetCheck()) + { + case BST_CHECKED: + GetApplication()->EnableAutoStart(m_hWnd, true); + break; + case BST_UNCHECKED: + GetApplication()->EnableAutoStart(m_hWnd, false); + break; + case BST_INDETERMINATE: + // Ignore + break; + } + + // Incorrectly documented in WTL + return true; +} + +LRESULT CAdvancedPage::OnStartService(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) +{ + GetApplication()->StartService(m_hWnd); + UpdateControls(); + return 0; +} + +LRESULT CAdvancedPage::OnStopService(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) +{ + GetApplication()->StopService(m_hWnd); + UpdateControls(); + return 0; +} + +LRESULT CAdvancedPage::OnWebAdmin(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) +{ + // Go to the config file because we might not have committed a change yet. + IniFile ini(GetApplication()->GetConfigPath()); + unsigned int port = ini.GetInteger(_T("general"), _T("port"), 9999); + + CString url; + url.Format(_T("http://localhost:%u/"), port); + + ::ShellExecute(m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWNORMAL); + return 0; +} + +void CAdvancedPage::OnServiceStatus(Service::Status old_status, Service::Status new_status) +{ + UpdateControls(new_status); +} + diff --git a/win32/FireflyShell/AdvancedPage.h b/win32/FireflyShell/AdvancedPage.h new file mode 100644 index 00000000..0ff53a8a --- /dev/null +++ b/win32/FireflyShell/AdvancedPage.h @@ -0,0 +1,98 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef ADVANCEDPAGE_H +#define ADVANCEDPAGE_H 1 + +#include "resource.h" +#include "ServiceControl.h" + +/// @todo Users shouldn't be able to paste non-numbers into the port number. + +class CAdvancedPage : + public CPropertyPageImpl, + public CWinDataExchange, + public ServiceStatusObserver +{ +public: + enum { IDD = IDD_PAGE_ADVANCED }; + +private: + typedef CPropertyPageImpl base; + + enum ServiceState + { + Pending = 0, + Running = 1, + Stopped = 2 + }; + + enum { TIMER_ID = 42 }; + + unsigned int m_server_port; + CUpDownCtrl m_port_spin; + CButton m_autostart_check; + + void UpdateControls(Service::Status status); + void UpdateControls(); + + // ServiceStatusObserver + void OnServiceStatus(Service::Status old_status, Service::Status new_status); + + BEGIN_MSG_MAP(CAdvancedPage) + MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) + COMMAND_HANDLER_EX(IDC_SERVERPORT, EN_CHANGE, OnChange) + COMMAND_HANDLER_EX(IDC_AUTOSTART, BN_CLICKED, OnClickAutostart) + COMMAND_ID_HANDLER(IDC_STARTSERVICE, OnStartService) + COMMAND_ID_HANDLER(IDC_STOPSERVICE, OnStopService) + COMMAND_ID_HANDLER(IDC_WEBADMIN, OnWebAdmin) + MSG_WM_DESTROY(OnDestroy) + CHAIN_MSG_MAP(base) + END_MSG_MAP() + + BEGIN_DDX_MAP(CAdvancedPage) + DDX_UINT(IDC_SERVERPORT, m_server_port); + DDX_CONTROL_HANDLE(IDC_PORTSPIN, m_port_spin); + DDX_CONTROL_HANDLE(IDC_AUTOSTART, m_autostart_check); + END_DDX_MAP() + + // MessageHandlers; + LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnStartService(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + LRESULT OnStopService(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + LRESULT OnWebAdmin(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + void OnDestroy(); + void OnTimer(UINT id, TIMERPROC proc); + int OnApply(); + void OnChange(UINT uCode, int nCtrlID, HWND hwndCtrl) + { + // Lots of things could have changed. + SetModified(); + } + void OnClickAutostart(UINT uCode, int nCtrlID, HWND hwndCtrl) + { + // When clicked revert to being a normal checkbox in case + // we were intermediate. + if (m_autostart_check.GetButtonStyle() != BS_AUTOCHECKBOX) + { + m_autostart_check.SetButtonStyle(BS_AUTOCHECKBOX); + m_autostart_check.SetCheck(TRUE); + } + OnChange(uCode, nCtrlID, hwndCtrl); + } +}; + +#endif // ADVANCEDPAGE_H \ No newline at end of file diff --git a/win32/FireflyShell/ConfigPage.cpp b/win32/FireflyShell/ConfigPage.cpp new file mode 100644 index 00000000..0c723d0f --- /dev/null +++ b/win32/FireflyShell/ConfigPage.cpp @@ -0,0 +1,90 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "ConfigPage.h" +#include "FireflyShell.h" +#include "IniFile.h" + +LRESULT CConfigPage::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) +{ + IniFile ini(GetApplication()->GetConfigPath()); + + m_server_name = ini.GetString(_T("general"), _T("servername"), _T("Firefly media server")); + m_media_path = ini.GetString(_T("general"), _T("mp3_dir"), _T("C:\\Music")); + m_password = ini.GetString(_T("general"), _T("password"), _T("")); + + // Do this before we try and use the controls. + DoDataExchange(false); + + const bool enable_password = !m_password.IsEmpty(); + m_protect_checkbox.SetCheck(enable_password); + EnableControls(); + return 0; +} + +void CConfigPage::EnableControls() +{ + const bool enable = GetApplication()->CanConfigure(); + GetDlgItem(IDC_SERVERNAME).EnableWindow(enable); + GetDlgItem(IDC_PATH).EnableWindow(enable); + GetDlgItem(IDC_PROTECT).EnableWindow(enable); + GetDlgItem(IDC_BROWSE).EnableWindow(enable); + + const bool enable_password = (m_protect_checkbox.GetCheck() != 0) && enable; + GetDlgItem(IDC_PASSWORD).EnableWindow(enable_password); + GetDlgItem(IDC_PASSWORD_PROMPT).EnableWindow(enable_password); + +} + +int CConfigPage::OnApply() +{ + ATLTRACE("CConfigPage::OnApply\n"); + if (!DoDataExchange(true)) + return false; + + IniFile ini(GetApplication()->GetConfigPath()); + ini.SetString(_T("general"), _T("servername"), m_server_name); + ini.SetString(_T("general"), _T("mp3_dir"), m_media_path); + ini.SetString(_T("general"), _T("password"), m_password); + + // Incorrectly documented in WTL + return true; +} + +LRESULT CConfigPage::OnBrowse(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) +{ + CFolderDialog folder; + + // Who cares if it fails. + DoDataExchange(true); + + folder.SetInitialFolder(m_media_path); + + if (folder.DoModal() == IDOK) + { + m_media_path = folder.GetFolderPath(); + DoDataExchange(false); + } + + return 0; +} + +void CConfigPage::OnClickProtect(UINT uCode, int nCtrlID, HWND hwndCtrl) +{ + EnableControls(); + SetModified(); +} \ No newline at end of file diff --git a/win32/FireflyShell/ConfigPage.h b/win32/FireflyShell/ConfigPage.h new file mode 100644 index 00000000..5a5f8640 --- /dev/null +++ b/win32/FireflyShell/ConfigPage.h @@ -0,0 +1,70 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef CONFIGPAGE_H +#define CONFIGPAGE_H 1 + +#include "resource.h" + +class CMainDlg; + +class CConfigPage : + public CPropertyPageImpl, + public CWinDataExchange +{ + typedef CPropertyPageImpl base; + +public: + enum { IDD = IDD_PAGE_BASIC }; + +private: + CString m_media_path; + CString m_server_name; + CString m_password; + CButton m_protect_checkbox; + + void EnableControls(); + + BEGIN_MSG_MAP(thisClass) + MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) + COMMAND_ID_HANDLER(IDC_BROWSE, OnBrowse) + COMMAND_HANDLER_EX(IDC_PROTECT, BN_CLICKED, OnClickProtect) + COMMAND_HANDLER_EX(IDC_PASSWORD, EN_CHANGE, OnChange) + COMMAND_HANDLER_EX(IDC_SERVERNAME, EN_CHANGE, OnChange) + COMMAND_HANDLER_EX(IDC_PATH, EN_CHANGE, OnChange) + CHAIN_MSG_MAP(base) + END_MSG_MAP() + + BEGIN_DDX_MAP(CConfigPage) + DDX_TEXT(IDC_PATH, m_media_path); + DDX_TEXT(IDC_SERVERNAME, m_server_name); + DDX_TEXT(IDC_PASSWORD, m_password); + DDX_CONTROL_HANDLE(IDC_PROTECT, m_protect_checkbox); + END_DDX_MAP() + + // Message handlers + LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnBrowse(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + void OnClickProtect(UINT uCode, int nCtrlID, HWND hwndCtrl); + int OnApply(); + void OnChange(UINT uCode, int nCtrlID, HWND hwndCtrl) + { + // Lots of things could have changed. + SetModified(); + } +}; + +#endif // CONFIGPAGE_H \ No newline at end of file diff --git a/win32/FireflyShell/DosPath.cpp b/win32/FireflyShell/DosPath.cpp new file mode 100644 index 00000000..5eb770e6 --- /dev/null +++ b/win32/FireflyShell/DosPath.cpp @@ -0,0 +1,173 @@ +/* + * (C) 1997, 2006 Mike Crowe + * + * License: Do what you like with it except claim that you wrote it. + */ + +#include "stdafx.h" +#include "dospath.h" +#include +#include + +CDosPath::CDosPath(const TCHAR *pszPath, int nFlags) +{ + SetPath(pszPath, nFlags); +} + +CDosPath::CDosPath(const CDosPath &old) +{ + m_drive = old.m_drive; + m_dir = old.m_dir; + m_file = old.m_file; + m_ext = old.m_ext; +} + +CDosPath &CDosPath::operator=(const CDosPath &old) +{ + m_drive = old.m_drive; + m_dir = old.m_dir; + m_file = old.m_file; + m_ext = old.m_ext; + return *this; +} + +CDosPath::~CDosPath() +{ +} + +void CDosPath::SplitPath(const TCHAR *path) +{ +#if USE_SECURE + _tsplitpath_s(path, m_drive.GetBufferSetLength(_MAX_DRIVE), _MAX_DRIVE, + m_dir.GetBufferSetLength(_MAX_DIR), _MAX_DIR, + m_file.GetBufferSetLength(_MAX_FNAME), _MAX_FNAME, + m_ext.GetBufferSetLength(_MAX_EXT), _MAX_EXT); +#else + _tsplitpath(path, m_drive.GetBufferSetLength(_MAX_DRIVE), + m_dir.GetBufferSetLength(_MAX_DIR), + m_file.GetBufferSetLength(_MAX_FNAME), + m_ext.GetBufferSetLength(_MAX_EXT)); +#endif + + m_ext.ReleaseBuffer(); + m_file.ReleaseBuffer(); + m_dir.ReleaseBuffer(); + m_drive.ReleaseBuffer(); +} + +void CDosPath::SetPath(const TCHAR *pszPath, int nFlags) +{ + if (nFlags & PATH_ONLY) + { + CString temp(pszPath); + temp += '\\'; + SplitPath(temp); + } + else + SplitPath(pszPath); +} + +CDosPath &CDosPath::operator|=(CDosPath &fullpath) +{ + if (m_drive.IsEmpty()) + { +// TRACE1("Inserting drive %s\n", fullpath.m_szDrive); + m_drive = fullpath.m_drive; + } + if (m_dir.IsEmpty()) + { +// TRACE1("Inserting directory %s\n", fullpath.m_szDir); + m_dir = fullpath.m_dir; + } + if (m_file.IsEmpty()) + { +// TRACE1("Inserting file %s\n", fullpath.m_szFile); + m_file = fullpath.m_file; + } + if (m_ext.IsEmpty()) + { +// TRACE1("Inserting extension %s\n", fullpath.m_szExt); + m_ext = fullpath.m_ext; + } + return *this; +} + +CDosPath CDosPath::operator|(CDosPath &fullpath) +{ + CDosPath temp(GetPath()); + temp |= fullpath; + return temp; +} + +CString CDosPath::GetPath() const +{ + CString temp; +#if USE_SECURE + _tmakepath_s(temp.GetBufferSetLength(_MAX_PATH), _MAX_PATH, m_drive, m_dir, m_file, m_ext); +#else + _tmakepath(temp.GetBufferSetLength(_MAX_PATH), m_drive, m_dir, m_file, m_ext); +#endif + temp.ReleaseBuffer(); + return temp; +} + +CString CDosPath::GetPathOnly() const +{ + CString temp; +#if USE_SECURE + _tmakepath_s(temp.GetBufferSetLength(_MAX_PATH), _MAX_PATH, m_drive, m_dir, NULL, NULL); +#else + _tmakepath(temp.GetBufferSetLength(_MAX_PATH), m_drive, m_dir, NULL, NULL); +#endif + temp.ReleaseBuffer(); + return temp; +} + +CDosPath CDosPath::CurrentPath() +{ + TCHAR szBuffer[_MAX_PATH]; + _tgetcwd(szBuffer, _MAX_PATH); + return CDosPath(szBuffer, PATH_ONLY); +} + +CDosPath CDosPath::AppPath() +{ + TCHAR szBuffer[_MAX_PATH]; +#ifdef _MFC + GetModuleFileName(AfxGetApp()->m_hInstance, szBuffer, _MAX_PATH); +#else + GetModuleFileName(GetModuleHandle(NULL), szBuffer, _MAX_PATH); +#endif + return CDosPath(szBuffer); +} + +CDosPath CDosPath::WindowsPath() +{ + TCHAR szBuffer[_MAX_PATH]; + GetWindowsDirectory(szBuffer, _MAX_PATH); + return CDosPath(szBuffer, PATH_ONLY); +} + +#ifdef MAKE_EXE +#include + +void main(int ac, char *av[]) +{ + if (ac != 3) + { + printf("Usage: dospath incomplete complete\n"); + return; + } + CDosPath a = av[1]; + CDosPath b = av[2]; + a |= b; + printf("%s |= %s = %s\n", av[1], av[2], (const char *) a.GetPath()); + printf("\n\n\n\n"); + a = CDosPath::CurrentPath(); + printf("Current = %s\n", (const char *)a.GetPath()); + a = CDosPath::AppPath(); + printf("App = %s\n", (const char *)a.GetPath()); + a = CDosPath::TempPath(); + printf("Temp = %s\n", (const char *)a.GetPath()); +} +#endif diff --git a/win32/FireflyShell/DosPath.h b/win32/FireflyShell/DosPath.h new file mode 100644 index 00000000..0704efff --- /dev/null +++ b/win32/FireflyShell/DosPath.h @@ -0,0 +1,51 @@ +/* + * (C) 1997, 2006 Mike Crowe + * + * License: Do what you like with it except claim that you wrote it. + */ + +#ifndef DOSPATH_H +#define DOSPATH_H + +class CDosPath +{ + CString m_drive; + CString m_dir; + CString m_file; + CString m_ext; + + void SplitPath(const TCHAR *buffer); + +public: + enum { PATH_ONLY = 1 }; + + CDosPath(const TCHAR *pszPath, int nFlags = 0); + CDosPath(const CDosPath &old); + ~CDosPath(); + + CDosPath &operator|=(CDosPath &fullpath); + CDosPath operator|(CDosPath &fullpath); + + CDosPath &operator=(const CDosPath &old); + CString GetPath() const; + CString GetPathOnly() const; + void SetPath(const TCHAR *pszBuffer, int nFlags = 0); + + static CDosPath CurrentPath(); + static CDosPath AppPath(); + static CDosPath TempPath(); +#ifdef _WINDOWS + static CDosPath WindowsPath(); +#endif + + CString GetFile() + { + return m_file; + } + CString GetExt() + { + return m_ext; + } +}; + +#endif // DOSPATH_H \ No newline at end of file diff --git a/win32/FireflyShell/FireflyShell.cpp b/win32/FireflyShell/FireflyShell.cpp new file mode 100644 index 00000000..efdbde61 --- /dev/null +++ b/win32/FireflyShell/FireflyShell.cpp @@ -0,0 +1,373 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "resource.h" +#include "FireflyShell.h" +#include "DosPath.h" +#include "ServiceControl.h" +#include "MainDlg.h" +#include "ServerEvents.h" +#include "IniFile.h" + +CAppModule _Module; + +#define RUN_KEY _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run") +#define RUN_VALUE _T("FireflyShell") + +Application::Application() + : m_dlg(NULL), m_server_events(&m_icon) +{ + CDosPath path = CDosPath::AppPath(); + CDosPath filename(_T("mt-daapd.conf")); + filename |= path; + m_config_path = filename.GetPath(); + ATLTRACE("Config path: %s\n", (const TCHAR *)m_config_path); + + // We don't care if this fails. We can deal with that later. + m_service.Open(_T("Firefly Media Server")); + + CheckCanConfigure(); + + m_unique_name = GenerateUniqueName(); + m_registered_activation_message = ::RegisterWindowMessage(m_unique_name); +} + +Application::~Application() +{ + ATLASSERT(m_dlg == NULL); +} + +void Application::CheckCanConfigure() +{ + IniFile ini(m_config_path); + m_configurable = ini.IsWritable(); +} + +int Application::Run(LPCTSTR lpstrCmdLine, int nCmdShow) +{ + if (ActivatePreviousInstance(lpstrCmdLine, nCmdShow)) + { + ATLTRACE(_T("Already running\n")); + return 0; + } + + CMessageLoop theLoop; + _Module.AddMessageLoop(&theLoop); + + if (!m_icon.Create()) + { + ATLTRACE(_T("Icon creation failed!\n")); + return 0; + } + + EnableServerEvents(true); + + if (ShowDialogAtStart(lpstrCmdLine, nCmdShow)) + Configure(); + + int nRet = theLoop.Run(); + + EnableServerEvents(false); + + m_icon.Destroy(); + + _Module.RemoveMessageLoop(); + return nRet; +} + +void Application::Exit() +{ + if (m_dlg) + { + m_dlg->DestroyWindow(); + delete m_dlg; + m_dlg = NULL; + } + ::PostQuitMessage(0); +} + +void Application::Configure() +{ + if (m_dlg) + { + m_dlg->ShowWindow(SW_RESTORE); + SetForegroundWindow(m_dlg->m_hWnd); + } + else + { + CheckCanConfigure(); + + // Other people may need to talk to the dialog while it exists. + CMainDlg dlg; + m_dlg = &dlg; + dlg.DoModal(); + m_dlg = NULL; + } +} + +void Application::StartService(HWND hwndParent) +{ + CWaitCursor wc; + ATLASSERT(m_service.CanControl()); + if (!m_service.CanControl()) + return; + + if (!m_service.StartAndWait()) + { + MessageBox(hwndParent, IDS_SERVERSTARTFAIL, MB_OK); + } +} + +void Application::StopService(HWND hwndParent) +{ + CWaitCursor wc; + ATLASSERT(m_service.CanControl()); + if (!m_service.CanControl()) + return; + + if (!m_service.StopAndWait()) + { + MessageBox(hwndParent, IDS_SERVERSTOPFAIL, MB_OK); + } +} + +void Application::RestartService(HWND hwndParent) +{ + CWaitCursor wc; + StopService(hwndParent); + StartService(hwndParent); +} + +bool Application::ShowDialogAtStart(LPCTSTR cmdline, int nCmdShow) +{ + if ((cmdline[0] == '-') && (cmdline[1] == 'q')) + return false; + + switch (nCmdShow) + { + case SW_RESTORE: + case SW_SHOW: + case SW_SHOWMAXIMIZED: + case SW_SHOWNORMAL: + case SW_SHOWDEFAULT: + case SW_MAX: + return true; + + default: + return false; + } +} + +BOOL CALLBACK Application::StaticWindowSearcher(HWND hwnd, LPARAM lparam) +{ + DWORD result; + LRESULT ok = ::SendMessageTimeout(hwnd, + lparam, + 0, 0, + SMTO_BLOCK | + SMTO_ABORTIFHUNG, + 200, + &result); + if (ok == 0) + return TRUE; // ignore this and continue + + // If we get the magic response then we must have found our Window + // so we can give up. + if (result == lparam) + return FALSE; + else + return TRUE; +} + +bool Application::ActivatePreviousInstance(LPCTSTR lpstrCmdLine, int nCmdShow) +{ + HANDLE h = ::CreateMutex(NULL, TRUE, m_unique_name); + const bool running = (GetLastError() == ERROR_ALREADY_EXISTS); + + if (h != NULL) + { + ::ReleaseMutex(h); + } + + // It seems that getting the other window to activate itself does + // actually work even though Windows anti-focus-stealing stuff + // could try and stop it. + if (running && ShowDialogAtStart(lpstrCmdLine, nCmdShow)) + { + EnumWindows(StaticWindowSearcher, m_registered_activation_message); + return true; + } + + return running; +} + +CString Application::GenerateUniqueName() +{ + // We need to allow one instance to run per desktop. See + // http://www.codeproject.com/cpp/avoidmultinstance.asp + + // First start with some application unique + CString s(_T("Firefly-67A72768-4154-417e-BFA0-FA9B50C342DE")); + + // Now append something desktop unique + DWORD len; + HDESK desktop = GetThreadDesktop(GetCurrentThreadId()); + BOOL result = GetUserObjectInformation(desktop, UOI_NAME, NULL, 0, &len); + DWORD err = ::GetLastError(); + if(!result && err == ERROR_INSUFFICIENT_BUFFER) + { /* NT/2000/XP */ + LPBYTE data = new BYTE[len]; + result = GetUserObjectInformation(desktop, UOI_NAME, data, len, &len); + s += _T("-"); + s += (LPCTSTR)data; + delete [ ] data; + } /* NT/2000/XP */ + else + { /* Win9x */ + s += _T("-Win9x"); + } /* Win9x */ + return s; +} + +void Application::EnableServerEvents(bool b) +{ + if (b) + m_server_events.Start(); + else + m_server_events.Stop(); +} + +CString Application::MakeRunKeyValue() +{ + CString required_path("\""); + required_path += CDosPath::AppPath().GetPath(); + required_path += "\" -q"; + return required_path; +} + +void Application::EnableAutoStart(HWND hwnd, bool enable) +{ + // First let's control the service. + int required_startup = enable ? SERVICE_AUTO_START : SERVICE_DISABLED; + + if (m_service.GetStartup() != required_startup) + { + if (!m_service.ConfigureStartup(required_startup)) + { + MessageBox(hwnd, IDS_FAILED_CONFIGURE_SERVICE, MB_OK); + } + } + + // Now let's set up the Run key. + HKEY hkey; + LONG result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, RUN_KEY, 0, KEY_SET_VALUE | STANDARD_RIGHTS_WRITE, &hkey); + if (result == ERROR_SUCCESS) + { + if (enable) + { + // We need to quote it because the path may contain spaces. + CString str = MakeRunKeyValue(); + result = RegSetValueEx(hkey, RUN_VALUE, 0UL, REG_SZ, reinterpret_cast(static_cast(str)), (str.GetLength() + 1) * sizeof(TCHAR)); + } + else + { + result = RegDeleteValue(hkey, RUN_VALUE); + if (result == ERROR_FILE_NOT_FOUND) + result = 0; + } + + if (result != ERROR_SUCCESS) + { + ATLTRACE("Error:%u\n", result); + MessageBox(hwnd, IDS_FAILED_CONFIGURE_STARTUP, MB_OK); + } + } +} + +int Application::IsAutoStartEnabled() const +{ + // Look at the service + int service_result = 2; + switch (m_service.GetStartup()) + { + case SERVICE_AUTO_START: + service_result = true; + break; + case SERVICE_DISABLED: + service_result = false; + break; + } + + // Look at the Run key + int run_result = 2; + HKEY hkey; + if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, RUN_KEY, 0, KEY_QUERY_VALUE | STANDARD_RIGHTS_READ, &hkey) == ERROR_SUCCESS) + { + DWORD dwType, cbData; + TCHAR buffer[_MAX_PATH + 1]; + cbData = (_MAX_PATH + 1) * sizeof(TCHAR); + if (::RegQueryValueEx(hkey, RUN_VALUE, NULL, &dwType, reinterpret_cast(buffer), &cbData) == ERROR_SUCCESS) + { + CString path(buffer, cbData - sizeof(TCHAR)); + ATLTRACE("Registry run key path: %s\n", (const TCHAR *)path); + + if (path == MakeRunKeyValue()) + run_result = true; + else + { + // It's there - but it isn't us that it will start. + run_result = 2; + } + } + else + { + // The key doesn't exist. + run_result = false; + } + } + else + { + run_result = false; + } + + // If the answers agree then return them. Otherwise we're indeterminate. + if (run_result == service_result) + return run_result; + else + return 2; +} + +int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) +{ + // this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used + ::DefWindowProc(NULL, 0, 0, 0L); + + AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls + + HRESULT hRes = _Module.Init(NULL, hInstance); + ATLASSERT(SUCCEEDED(hRes)); + + int nRet; + { + // Application object is destroyed prior to undoing everything above. + Application app; + nRet = app.Run(lpstrCmdLine, nCmdShow); + } + + _Module.Term(); + return nRet; +} diff --git a/win32/FireflyShell/FireflyShell.h b/win32/FireflyShell/FireflyShell.h new file mode 100644 index 00000000..f43190cf --- /dev/null +++ b/win32/FireflyShell/FireflyShell.h @@ -0,0 +1,154 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef FIREFLYSHELL_H +#define FIREFLYSHELL_H 1 + +#include "singleton.h" +#include "NotifyIcon.h" +#include "ServiceControl.h" +#include +#include +#include "ServerEvents.h" + +class CMainDlg; + +/// The main application. Not a window. +class Application : public Singleton +{ + CNotifyIcon m_icon; + CMainDlg *m_dlg; + CString m_config_path; + Service m_service; + ServiceStatusMonitor m_service_monitor; + ServerEvents m_server_events; + CString m_unique_name; + UINT m_registered_activation_message; + bool m_configurable; + + /// Returns true if a previous instance was found. Only + /// actually activates a previous instance if nCmdShow + /// indicates so. + bool ActivatePreviousInstance(LPCTSTR cmdline, int nCmdShow); + + /// Helper for ActivatePreviousInstance. + static BOOL CALLBACK Application::StaticWindowSearcher(HWND hwnd, LPARAM lparam); + + /// Generate a unique name that can be used to detect multiple instances. + static CString GenerateUniqueName(); + + // Depending on how the application was launched we display the dialog or not. + static bool ShowDialogAtStart(LPCTSTR cmdline, int nCmdShow); + + static CString MakeRunKeyValue(); + +public: + Application(); + ~Application(); + + /// Gets the application going + int Run(LPCTSTR lpstrCmdLine, int nCmdShow); + + /// mt-daapd.conf path + CString GetConfigPath() + { + return m_config_path; + } + + /// Registered message used to activate other instances + UINT GetRegisteredActivationMessage() const + { + return m_registered_activation_message; + } + + // User actions + void Configure(); + void Exit(); + + // Service control + void StartService(HWND hwndParent); + void StopService(HWND hwndParent); + void RestartService(HWND hwndParent); + + Service::Status GetServiceStatus() + { + Service::Status status; + if (m_service.IsOpen()) + { + m_service.GetStatus(&status); + } + return status; + } + + // Expensive - only do it just before displaying the dialog box. + void CheckCanConfigure(); + + // Cheap. + bool CanConfigure() const + { + // We need to both rewrite the config file and + // control the service if we're going to be + // useful. + return m_configurable && m_service.CanControl(); + } + + bool CanControlService() const + { + return m_service.CanControl(); + } + + void CheckServiceStatus() + { + m_service_monitor.Poll(&m_service); + } + + void ServiceStatusSubscribe(ServiceStatusObserver *obs) + { + m_service_monitor.Subscribe(obs); + } + + void ServiceStatusUnsubscribe(ServiceStatusObserver *obs) + { + m_service_monitor.Unsubscribe(obs); + } + + CString GetServiceBinaryPath() const + { + return m_service.GetBinaryPath(); + } + + /// Used to disable listening for events from the server when this user is + /// not active. + void EnableServerEvents(bool); + + /// Enable/disable automatic startup of service and FireflyShell. + void EnableAutoStart(HWND, bool); + + /// Reports 0 for disabled, 1 for enabled, 2 for indeterminate + int IsAutoStartEnabled() const; + + int MessageBox(HWND hwnd, UINT id, UINT flags) + { + CString title, text; + ATLVERIFY(title.LoadString(IDR_MAINFRAME)); + ATLVERIFY(text.LoadString(id)); + return ::MessageBox(hwnd, text, title, flags); + } +}; + +inline Application *GetApplication() { return Application::GetInstance(); } + +#endif // FIREFLYSHELL_H diff --git a/win32/FireflyShell/FireflyShell.rc b/win32/FireflyShell/FireflyShell.rc new file mode 100644 index 00000000..31e9a262 --- /dev/null +++ b/win32/FireflyShell/FireflyShell.rc @@ -0,0 +1,301 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "atlres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""atlres.h""\r\n" +END + +3 TEXTINCLUDE +BEGIN + "#ifndef APSTUDIO_INVOKED\r\n" + "#include ""version.rc""\r\n" + "#endif // !APSTUDIO_INVOKED\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON "res\\FireflyShell.ico" +IDI_SHELL_RUNNING ICON "res\\shellrunning.ico" +IDI_SHELL_STOPPED ICON "res\\shellstopped.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PAGE_ABOUT DIALOGEX 0, 0, 221, 223 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,93,18,20,20 + CTEXT "Firefly Media Server",IDC_STATIC,7,41,207,11 + LTEXT "For help, tips and troubleshooting advice visit the Firefly Media Server website.",IDC_STATIC,12,79,119,23 + PUSHBUTTON "Visit &Website...",IDC_WEBSITE,139,82,70,14 + GROUPBOX "Version information",IDC_STATIC,7,114,207,102 + CONTROL "",IDC_VERSIONLIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,12,126,197,63 + PUSHBUTTON "&Copy to Clipboard",IDC_COPY,139,194,70,14 +END + +IDD_PAGE_BASIC DIALOGEX 0, 0, 222, 230 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Library" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Library &Name",IDC_STATIC,7,24,72,8 + EDITTEXT IDC_SERVERNAME,61,21,152,14,ES_AUTOHSCROLL + LTEXT "Media &Location",IDC_STATIC,7,60,48,8 + EDITTEXT IDC_PATH,61,57,152,14,ES_AUTOHSCROLL + PUSHBUTTON "&Browse...",IDC_BROWSE,163,76,50,14 + GROUPBOX "Security",IDC_STATIC,7,113,206,113 + CONTROL "&Protect Firefly media library with a password",IDC_PROTECT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,128,187,10 + LTEXT "Password:",IDC_PASSWORD_PROMPT,18,157,34,8 + EDITTEXT IDC_PASSWORD,61,156,148,14,ES_PASSWORD | ES_AUTOHSCROLL + LTEXT "Explanatory text about passwords go here. Why you'd want a password and what are the implications 'n' stuff.",IDC_STATIC,18,180,188,39,SS_NOPREFIX +END + +IDD_PAGE_LOG DIALOGEX 0, 0, 222, 220 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Log" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_LOG,4,4,214,212,ES_AUTOHSCROLL | ES_READONLY +END + +IDD_PAGE_ADVANCED DIALOGEX 0, 0, 222, 220 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Server" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Server Status",IDC_STATIC,6,7,210,82 + LTEXT "Current state of the server goes here",IDC_SERVERSTATE,12,24,200,25 + PUSHBUTTON "&Stop Server",IDC_STOPSERVICE,160,49,50,14 + PUSHBUTTON "&Start Server",IDC_STARTSERVICE,160,49,50,14 + CONTROL "&Start Firefly when Windows starts",IDC_AUTOSTART, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,73,196,10 + GROUPBOX "Advanced",IDC_STATIC,6,93,210,61 + LTEXT "Server port number",IDC_STATIC,12,108,122,11 + EDITTEXT IDC_SERVERPORT,155,106,42,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_PORTSPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,197,106,11,14 + LTEXT "You may need to change this if another program is already using this port.",IDC_STATIC,12,129,196,23 + GROUPBOX "Web administration",IDC_STATIC,6,159,210,54 + LTEXT "Firefly Media Server also provides a web administration interface.",IDC_STATIC,12,177,140,20 + PUSHBUTTON "&Open",IDC_WEBADMIN,160,194,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PAGE_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 214 + VERTGUIDE, 12 + VERTGUIDE, 131 + VERTGUIDE, 139 + VERTGUIDE, 209 + TOPMARGIN, 7 + BOTTOMMARGIN, 216 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS +BEGIN + "N", ID_FILE_NEW, VIRTKEY, CONTROL + "O", ID_FILE_OPEN, VIRTKEY, CONTROL + "S", ID_FILE_SAVE, VIRTKEY, CONTROL + "P", ID_FILE_PRINT, VIRTKEY, CONTROL + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL + "X", ID_EDIT_CUT, VIRTKEY, CONTROL + "C", ID_EDIT_COPY, VIRTKEY, CONTROL + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT + VK_F6, ID_NEXT_PANE, VIRTKEY + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDM_CONTEXT MENU +BEGIN + POPUP "" + BEGIN + MENUITEM "&Configure Firefly Media Server...", ID_CONFIGURE + MENUITEM "E&xit", ID_EXIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDR_MAINFRAME "Firefly Media Server" + IDS_SERVER_RUNNING "The Firefly media server is currently running." + IDS_SERVER_STOPPED "The Firefly media server is not currently running." + IDS_SERVERSTARTFAIL "Failed to start Firefly Media Server." + IDS_SERVERSTOPFAIL "Failed to stop Firefly Media Server." + IDS_SERVER_PENDING "The Firefly media server is currently busy." + IDS_SERVER_START "&Start" + IDS_SERVER_STOP "&Stop" + IDS_SCAN_START "Scanning for media is in progress\n\nThis may take some time." + IDS_SCAN_STOP "Scanning for media is complete." + IDS_NOT_ADMIN "You must be logged in as a local administrator to start and stop the server." + IDS_QUERYSERVERRESTART "Changing the configuration will require a restart of the Firefly Media Server. Are you sure you want to do this?" + IDS_VERSIONINFO_DESCRIPTION "Description" + IDS_VERSIONINFO_VERSION "Version" + IDS_VERSIONINFO_PATH "Path" + IDS_FAILED_CONFIGURE_SERVICE "Failed to reconfigure service." +END + +STRINGTABLE +BEGIN + ID_FILE_NEW "Create a new document\nNew" + ID_FILE_OPEN "Open an existing document\nOpen" + ID_FILE_CLOSE "Close the active document\nClose" + ID_FILE_SAVE "Save the active document\nSave" + ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" + ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" + ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" + ID_FILE_PRINT "Print the active document\nPrint" + ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" +END + +STRINGTABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save documents\nExit" +END + +STRINGTABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" +END + +STRINGTABLE +BEGIN + ID_WINDOW_NEW "Open another window for the active document\nNew Window" + ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" + ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade Windows" + ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE +BEGIN + ATL_IDS_SCSIZE "Change the window size" + ATL_IDS_SCMOVE "Change the window position" + ATL_IDS_SCMINIMIZE "Reduce the window to an icon" + ATL_IDS_SCMAXIMIZE "Enlarge the window to full size" + ATL_IDS_SCNEXTWINDOW "Switch to the next document window" + ATL_IDS_SCPREVWINDOW "Switch to the previous document window" + ATL_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE +BEGIN + ATL_IDS_SCRESTORE "Restore the window to normal size" + ATL_IDS_SCTASKLIST "Activate Task List" + ATL_IDS_MDICHILD "Activate this window" +END + +STRINGTABLE +BEGIN + ATL_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE +BEGIN + ATL_IDS_MRU_FILE "Open this document" +END + +STRINGTABLE +BEGIN + IDS_FAILED_CONFIGURE_STARTUP "Failed to reconfigure startup application." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/win32/FireflyShell/FireflyShell.vcproj b/win32/FireflyShell/FireflyShell.vcproj new file mode 100644 index 00000000..2a526490 --- /dev/null +++ b/win32/FireflyShell/FireflyShell.vcproj @@ -0,0 +1,370 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/win32/FireflyShell/IniFile.h b/win32/FireflyShell/IniFile.h new file mode 100644 index 00000000..412dc4e8 --- /dev/null +++ b/win32/FireflyShell/IniFile.h @@ -0,0 +1,68 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef INIFILE_H +#define INIFILE_H +#include "DosPath.h" + +class IniFile +{ + CString m_path; + +public: + explicit IniFile(const CString &path) + : m_path(path) + { + } + + CString GetString(const TCHAR *section, const TCHAR *key, const TCHAR *defstring) + { + CString str; + ::GetPrivateProfileString(section, key, defstring, str.GetBuffer(512), 512, m_path); + str.ReleaseBuffer(); + return str; + } + + bool SetString(const TCHAR *section, const TCHAR *key, const TCHAR *value) + { + return ::WritePrivateProfileString(section, key, value, m_path) != 0; + } + + int GetInteger(const TCHAR *section, const TCHAR *key, int defvalue) + { + return ::GetPrivateProfileInt(section, key, defvalue, m_path); + } + + bool SetInteger(const TCHAR *section, const TCHAR *key, int value) + { + CString str; + str.Format(_T("%d"), value); + return ::WritePrivateProfileString(section, key, str, m_path) != 0; + } + + bool IsWritable() const + { + if (::WritePrivateProfileString(_T("Writability Test"), _T("Writability Test"), _T("Test"), m_path)) + { + // Remove it then. + ::WritePrivateProfileString(_T("Writability Test"), NULL, NULL, m_path); + return true; + } + else + return false; + } +}; +#endif // INIFILE_H diff --git a/win32/FireflyShell/LogPage.cpp b/win32/FireflyShell/LogPage.cpp new file mode 100644 index 00000000..49e93f75 --- /dev/null +++ b/win32/FireflyShell/LogPage.cpp @@ -0,0 +1,23 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "LogPage.h" + +LRESULT CLogPage::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) +{ + return 0; +} diff --git a/win32/FireflyShell/LogPage.h b/win32/FireflyShell/LogPage.h new file mode 100644 index 00000000..ff004821 --- /dev/null +++ b/win32/FireflyShell/LogPage.h @@ -0,0 +1,41 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef LOGPAGE_H +#define LOGPAGE_H 1 + +#include "resource.h" + +class CLogPage : + public CPropertyPageImpl +{ + typedef CPropertyPageImpl base; + +public: + enum { IDD = IDD_PAGE_LOG }; + +private: + + // Message Handlers + BEGIN_MSG_MAP(CLogPage) + MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) + CHAIN_MSG_MAP(base) + END_MSG_MAP() + + LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); +}; + +#endif // LOGPAGE_H \ No newline at end of file diff --git a/win32/FireflyShell/MainDlg.cpp b/win32/FireflyShell/MainDlg.cpp new file mode 100644 index 00000000..6fc16afe --- /dev/null +++ b/win32/FireflyShell/MainDlg.cpp @@ -0,0 +1,59 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "resource.h" + +#include "MainDlg.h" +#include "FireflyShell.h" + +CMainDlg::CMainDlg() +{ + this->AddPage(m_pageConfig); + this->AddPage(m_pageAdvanced); + //this->AddPage(m_pageLog); + this->AddPage(m_pageAbout); + + ATLVERIFY(m_strTitle.LoadString(IDR_MAINFRAME)); + this->SetTitle(m_strTitle); +} + +LRESULT CMainDlg::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + bool restart_service = false; + if (HIWORD(wParam) == BN_CLICKED) + { + const UINT id = LOWORD(wParam); + + if ((id == IDOK || id == ID_APPLY_NOW) + && GetDlgItem(ID_APPLY_NOW).IsWindowEnabled() + && GetApplication()->GetServiceStatus().IsRunning()) + { + CString title, text; + title.LoadString(IDR_MAINFRAME); + text.LoadString(IDS_QUERYSERVERRESTART); + if (MessageBox(text, title, MB_YESNO) != IDYES) + return 0; + restart_service = true; + } + } + + LRESULT result = base::OnCommand(uMsg, wParam, lParam, bHandled); + if (restart_service) + GetApplication()->RestartService(m_hWnd); + + return result; +} diff --git a/win32/FireflyShell/MainDlg.h b/win32/FireflyShell/MainDlg.h new file mode 100644 index 00000000..14f28923 --- /dev/null +++ b/win32/FireflyShell/MainDlg.h @@ -0,0 +1,47 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef MAINDLG_H +#define MAINDLG_H 1 + +#include "ConfigPage.h" +#include "AdvancedPage.h" +#include "LogPage.h" +#include "AboutPage.h" + +class CMainDlg : public CPropertySheetImpl +{ + typedef CPropertySheetImpl base; + + CString m_strTitle; + + CConfigPage m_pageConfig; + CAdvancedPage m_pageAdvanced; + CLogPage m_pageLog; + CAboutPage m_pageAbout; + + BEGIN_MSG_MAP(CMainDlg) + MESSAGE_HANDLER(WM_COMMAND, OnCommand) + CHAIN_MSG_MAP(base) + END_MSG_MAP() + + LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + +public: + CMainDlg(); +}; + +#endif // MAINDLG_H \ No newline at end of file diff --git a/win32/FireflyShell/NotifyIcon.cpp b/win32/FireflyShell/NotifyIcon.cpp new file mode 100644 index 00000000..3aea1b5a --- /dev/null +++ b/win32/FireflyShell/NotifyIcon.cpp @@ -0,0 +1,270 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "resource.h" +#include "NotifyIcon.h" +#include "FireflyShell.h" + +CNotifyIcon::CNotifyIcon() +{ + ZeroMemory(&m_nid, sizeof(NOTIFYICONDATA)); + m_nid.cbSize = sizeof(NOTIFYICONDATA); + m_nid.uID = ID_SHELLNOTIFY; + + m_running_icon = AtlLoadIcon(IDI_SHELL_RUNNING); + m_stopped_icon = AtlLoadIcon(IDI_SHELL_STOPPED); +} + +BOOL CNotifyIcon::Create() +{ + m_registered_activation_message = GetApplication()->GetRegisteredActivationMessage(); + + RECT rect = {0, 0, 0, 0}; + + // Hidden window. + if (base::Create(NULL, rect, _T("FireflyShellNotifyIconHidden"), WS_POPUP)) + { + m_nid.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + InflictIconState(); + + //wcsncpy(niData.szTip, TEXT("Foo?"), sizeof(niData.szTip)); + m_nid.hWnd = m_hWnd; + m_nid.uCallbackMessage = PRIVATE_WM_NOTIFYICON; + + Shell_NotifyIcon(NIM_ADD, &m_nid); + + SetTimer(TIMER_ID, 5000, NULL); + + GetApplication()->ServiceStatusSubscribe(this); + + EnableUserSwitchNotifications(); + + return TRUE; + } + + return FALSE; +} + +void CNotifyIcon::Destroy() +{ + GetApplication()->ServiceStatusUnsubscribe(this); + KillTimer(TIMER_ID); + Shell_NotifyIcon(NIM_DELETE, &m_nid); + DestroyIcon(m_nid.hIcon); + + + base::DestroyWindow(); +} + +void CNotifyIcon::PopupBalloon(UINT title_id, UINT text_id, DWORD flags) +{ + CString title, text; + title.LoadString(title_id); + text.LoadString(text_id); + + m_nid.uFlags |= NIF_INFO; + + SafeStringCopy(m_nid.szInfoTitle, title, 64); + SafeStringCopy(m_nid.szInfo, text, 256); + + m_nid.dwInfoFlags = flags; + m_nid.uTimeout = 10000; + Shell_NotifyIcon(NIM_MODIFY, &m_nid); +} + +void CNotifyIcon::Update() +{ + InflictIconState(); + // I suspect we'll need this line too. + // m_nid.uFlags &= ~NIF_INFO; + Shell_NotifyIcon(NIM_MODIFY, &m_nid); +} + +void CNotifyIcon::InflictIconState() +{ + // Will the icons leak? + Service::Status status = GetApplication()->GetServiceStatus(); + UINT state_id; + if (status.IsPending()) + { + state_id = IDS_SERVER_PENDING; + m_nid.hIcon = m_stopped_icon; // As good as any? + } + else if (status.IsRunning()) + { + state_id = IDS_SERVER_RUNNING; + m_nid.hIcon = m_running_icon; + } + else + { + state_id = IDS_SERVER_STOPPED; + m_nid.hIcon = m_stopped_icon; + } + + CString tip; + tip.LoadString(state_id); + SafeStringCopy(m_nid.szTip, tip, 64); +} + +void CNotifyIcon::OnServiceStatus(Service::Status old_status, Service::Status new_status) +{ + Update(); +} + +void CNotifyIcon::OnTimer(UINT id, TIMERPROC proc) +{ + if (id == TIMER_ID) + { + GetApplication()->CheckServiceStatus(); + } +} + +LRESULT CNotifyIcon::OnNotifyIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + switch (lParam) + { + case WM_LBUTTONDBLCLK: + GetApplication()->Configure(); + bHandled = true; + return 0L; + case WM_RBUTTONDOWN: + case WM_CONTEXTMENU: + OnContextMenu(); + bHandled = true; + return 0L; + } + + return 0L; +} + +void CNotifyIcon::OnContextMenu() +{ + HMENU hMenu; + + HINSTANCE hInstance = _Module.GetResourceInstance(); + hMenu = ::LoadMenu(hInstance, MAKEINTRESOURCE(IDM_CONTEXT)); + + POINT pt; + GetCursorPos(&pt); + + // See TrackPopupMenu in MSDN. + SetForegroundWindow(m_hWnd); + //::SetForegroundWindow(m_hWnd); + HMENU hPopup = GetSubMenu(hMenu, 0); + ::SetMenuDefaultItem(hPopup, ID_CONFIGURE, FALSE); + TrackPopupMenu(hPopup, TPM_LEFTALIGN | TPM_BOTTOMALIGN, pt.x, pt.y, 0, m_hWnd, NULL); + ::PostMessage(m_hWnd, WM_NULL, 0, 0); + + ::DestroyMenu(hMenu); +} + +LRESULT CNotifyIcon::OnConfigure(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) +{ + GetApplication()->Configure(); + return 0; +} + +LRESULT CNotifyIcon::OnExit(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) +{ + /// @todo + Application::GetInstance()->Exit(); + return 0; +} + +LRESULT CNotifyIcon::OnRegisteredActivation(UINT, WPARAM, LPARAM, BOOL &bHandled) +{ + ATLTRACE(_T("Activate\n")); + bHandled = true; + GetApplication()->Configure(); + + // We return a magic number so that the caller knows we've been found + // and can give up. + return m_registered_activation_message; +} + +void CNotifyIcon::OnServerEvent(UINT32 id, UINT32 intval, const CString &strval) +{ + // Note that we're running on a different thread here. We need to punt + // the event over to the main thread using SendMessage. + switch (id) + { + case 0: + break; + case 1: + case 2: + SendMessage(WM_SERVEREVENT, id); + break; + default: + ATLASSERT(false); + break; + } +} + +LRESULT CNotifyIcon::OnServerEvent(UINT, WPARAM wparam, LPARAM, BOOL &bHandled) +{ + bHandled = true; + switch (wparam) + { + case 1: + PopupBalloon(IDR_MAINFRAME, IDS_SCAN_START); + break; + case 2: + PopupBalloon(IDR_MAINFRAME, IDS_SCAN_STOP); + break; + } + return 0; +} + +void CNotifyIcon::EnableUserSwitchNotifications() +{ + HMODULE h = ::LoadLibrary(_T("WtsApi32.dll")); + if (h) + { + typedef BOOL (WINAPI *Proc)(HWND, DWORD); + Proc fn = reinterpret_cast(GetProcAddress(h, "WTSRegisterSessionNotification")); + if (fn) + { + (*fn)(m_hWnd, NOTIFY_FOR_THIS_SESSION); + } + ::FreeLibrary(h); + } +} + +LRESULT CNotifyIcon::OnSessionChange(UINT, WPARAM wparam, LPARAM, BOOL &bHandled) +{ + // Because only one process can get events through the mailslot we + // disconnect from it when the user uses XP fast user switching to + // switch to a different user. + switch (wparam) + { + case WTS_CONSOLE_CONNECT: + case WTS_REMOTE_CONNECT: + ATLTRACE("SESSION CONNECT\n"); + GetApplication()->EnableServerEvents(true); + break; + + case WTS_CONSOLE_DISCONNECT: + case WTS_REMOTE_DISCONNECT: + ATLTRACE("SESSION DISCONNECT\n"); + GetApplication()->EnableServerEvents(false); + break; + } + bHandled = true; + return 0; +} + diff --git a/win32/FireflyShell/NotifyIcon.h b/win32/FireflyShell/NotifyIcon.h new file mode 100644 index 00000000..81179dbb --- /dev/null +++ b/win32/FireflyShell/NotifyIcon.h @@ -0,0 +1,81 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef NOTIFYICON_H +#define NOTIFYICON_H + +#include "ServiceControl.h" +#include "ServerEvents.h" + +class CNotifyIcon + : public CWindowImpl, + public ServiceStatusObserver, + public ServerEvents::Observer +{ + typedef CWindowImpl base; + + NOTIFYICONDATA m_nid; + HICON m_running_icon; + HICON m_stopped_icon; + UINT m_registered_activation_message; + + enum { TIMER_ID = 43 }; + enum { WM_SERVEREVENT = WM_APP + 42 }; + + enum { PRIVATE_WM_NOTIFYICON = WM_USER + 42 }; + + BEGIN_MSG_MAP(CNotifyIcon) + MESSAGE_HANDLER(PRIVATE_WM_NOTIFYICON, OnNotifyIconMessage) + COMMAND_ID_HANDLER(ID_CONFIGURE, OnConfigure) + COMMAND_ID_HANDLER(ID_EXIT, OnExit) + MESSAGE_HANDLER(m_registered_activation_message, OnRegisteredActivation) + MESSAGE_HANDLER(WM_SERVEREVENT, OnServerEvent) + MESSAGE_HANDLER(WM_WTSSESSION_CHANGE, OnSessionChange) + MSG_WM_TIMER(OnTimer) + END_MSG_MAP() + + // Message handlers + void OnContextMenu(); + LRESULT OnNotifyIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnConfigure(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + LRESULT OnExit(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + LRESULT OnRegisteredActivation(UINT, WPARAM, LPARAM, BOOL &bHandled); + LRESULT OnServerEvent(UINT, WPARAM, LPARAM, BOOL &bHandled); + LRESULT OnSessionChange(UINT, WPARAM, LPARAM, BOOL &bHandled); + void OnTimer(UINT id, TIMERPROC proc); + + void PopupBalloon(UINT title_id, UINT text_id, DWORD flags = NIIF_INFO); + + void InflictIconState(); + void Update(); + + // Terminal services stuff on XP. + void EnableUserSwitchNotifications(); + + // ServiceStatusObserver + void OnServiceStatus(Service::Status old_status, Service::Status new_status); + + // ServerEvents::Observer + void OnServerEvent(UINT32 id, UINT32 intval, const CString &str); + +public: + CNotifyIcon(); + BOOL Create(); + void Destroy(); + +}; + +#endif // NOTIFYICON_H \ No newline at end of file diff --git a/win32/FireflyShell/README.txt b/win32/FireflyShell/README.txt new file mode 100644 index 00000000..2ca6df5d --- /dev/null +++ b/win32/FireflyShell/README.txt @@ -0,0 +1,17 @@ +The FireflyShell source code is distributed under the terms of the GNU +General Public License Version 2 as published by the Free Software +Foundation. You can find a copy of this license in the LICENSE.txt +file. + +This program uses the Microsoft Windows Template Library which is released +under the Common Public License. The copyright holders of FireflyShell +explicitly grant the additional right to distribute binaries produced from +source code derived from FireflyShell that are linked with the Microsoft +Windows Template Library. + +Note that you may not be able to incorporate third party GNU General Public +Licensed code into FireflyShell and then distribute binaries unless you have +a similar additional right from the copyright holder. + +You can find the Windows Template Library at +http://sourceforge.net/projects/wtl/ diff --git a/win32/FireflyShell/ServerEvents.cpp b/win32/FireflyShell/ServerEvents.cpp new file mode 100644 index 00000000..7228c83e --- /dev/null +++ b/win32/FireflyShell/ServerEvents.cpp @@ -0,0 +1,142 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "ServerEvents.h" + +ServerEvents::ServerEvents(Observer *obs) : + m_thread(INVALID_HANDLE_VALUE), + m_mailslot(INVALID_HANDLE_VALUE), + m_obs(obs) +{ +} + +ServerEvents::~ServerEvents() +{ + ATLASSERT(m_mailslot == INVALID_HANDLE_VALUE); + ATLASSERT(m_thread == INVALID_HANDLE_VALUE); +} + +bool ServerEvents::Start() +{ + ATLASSERT(m_mailslot == INVALID_HANDLE_VALUE); + ATLASSERT(m_thread == INVALID_HANDLE_VALUE); + ATLASSERT(m_obs != NULL); + + m_mailslot = ::CreateMailslot(_T("\\\\.\\mailslot\\FireflyMediaServer--67A72768-4154-417e-BFA0-FA9B50C342DE"), 0, MAILSLOT_WAIT_FOREVER, NULL); + + if (m_mailslot != INVALID_HANDLE_VALUE) + { + //m_thread = ::CreateThread(NULL, 0, &StaticThreadProc, this, 0, &thread_id); + m_thread = (HANDLE)_beginthreadex(NULL, 0, &StaticThreadProc, this, 0, NULL); + if (m_thread == NULL) + { + // Failed + ATLTRACE("beginthreadex failed: %d\n", errno); + ::CloseHandle(m_mailslot); + m_mailslot = INVALID_HANDLE_VALUE; + return false; + } + } + else + { + return false; + } + + return true; +} + +void ServerEvents::Stop() +{ + ATLASSERT(m_mailslot != INVALID_HANDLE_VALUE); + ATLASSERT(m_thread != INVALID_HANDLE_VALUE); + + // Force the thread to give up. This could be done with a separate event + // and overlapped IO but this is cheap. + CloseHandle(m_mailslot); + m_mailslot = INVALID_HANDLE_VALUE; + + // Wait for it to finish. + WaitForSingleObject(m_thread, INFINITE); + CloseHandle(m_thread); + m_thread = INVALID_HANDLE_VALUE; +} + +//DWORD ServerEvents::StaticThreadProc(LPVOID param) +unsigned ServerEvents::StaticThreadProc(void *param) +{ + return reinterpret_cast(param)->ThreadProc(); +} + +DWORD ServerEvents::ThreadProc() +{ + const size_t BUFFER_SIZE = 65536; + void *buffer = operator new(BUFFER_SIZE); + + bool finished = false; + while (!finished) + { + DWORD bytes_read; + if (ReadFile(m_mailslot, buffer, BUFFER_SIZE, &bytes_read, NULL)) + { + TCHAR *b = (TCHAR *)buffer; + b[bytes_read/sizeof(TCHAR)] = 0; + ATLTRACE("%ls\n", b); + + OnEvent(buffer, bytes_read); + } + else + { + ATLTRACE("Read failed: error %d\n", GetLastError()); + finished = true; + } + } + return 0; +} + +void ServerEvents::OnEvent(const void *buffer, size_t bytes_received) +{ + const BYTE *received = reinterpret_cast(buffer); + + if (bytes_received >= 12) + { + UINT32 packet_size = received[0] | (received[1] << 8) | (received[2] << 16) | (received[3] << 24); + UINT32 id = received[4] | (received[5] << 8) | (received[6] << 16) | (received[7] << 24); + UINT32 intval = received[8] | (received[9] << 8) | (received[10] << 16) | (received[11] << 24); + + size_t string_length = bytes_received - 12; + + if ((packet_size < bytes_received) && (packet_size >= 12)) + string_length = packet_size - 12; + + CString str; + if (string_length > 0) + { +#ifdef UNICODE + // It might be less that string_length long after conversion but it shouldn't be more unless + // our codepage is extremely weird. + + str.ReleaseBuffer(MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, reinterpret_cast(received + 12), + string_length, str.GetBufferSetLength(string_length), string_length)); +#else + SafeStringCopy(str.GetBufferSetLength(string_length), received + 12, string_length); +#endif + str.ReleaseBuffer(string_length); + } + + m_obs->OnServerEvent(id, intval, str); + } +} \ No newline at end of file diff --git a/win32/FireflyShell/ServerEvents.h b/win32/FireflyShell/ServerEvents.h new file mode 100644 index 00000000..ad369ba4 --- /dev/null +++ b/win32/FireflyShell/ServerEvents.h @@ -0,0 +1,55 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef SERVEREVENTS_H +#define SERVEREVENTS_H + +class ServerEvents +{ +public: + /// Note that the observer is called on the wrong thread. + class Observer + { + public: + virtual void OnServerEvent(UINT32 id, UINT32 intval, const CString &str) = 0; + }; + +private: + + HANDLE m_thread; + HANDLE m_mailslot; + Observer *m_obs; + + static unsigned __stdcall StaticThreadProc(void *); + DWORD ThreadProc(); + + void OnEvent(const void *buffer, size_t length); + +public: + ServerEvents(Observer *obs); + ~ServerEvents(); + + void SetObserver(Observer *obs) + { + ATLASSERT(m_obs == NULL); + m_obs = obs; + } + + bool Start(); + void Stop(); +}; + +#endif // SERVICE \ No newline at end of file diff --git a/win32/FireflyShell/ServiceControl.cpp b/win32/FireflyShell/ServiceControl.cpp new file mode 100644 index 00000000..6141e967 --- /dev/null +++ b/win32/FireflyShell/ServiceControl.cpp @@ -0,0 +1,205 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "ServiceControl.h" + +bool Service::Open(const TCHAR *name) +{ + Close(); + + const DWORD ADMIN_ACCESS = SC_MANAGER_ALL_ACCESS; + const DWORD USER_ACCESS = SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE | STANDARD_RIGHTS_READ; + + DWORD dwDesiredAccess = ADMIN_ACCESS; + + m_sc_manager = ::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, dwDesiredAccess); + if (!m_sc_manager) + { + dwDesiredAccess = USER_ACCESS; + m_sc_manager = ::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, dwDesiredAccess); + m_can_control = false; + } + else + { + m_can_control = true; + } + + if (m_sc_manager) + { + m_sc_service = ::OpenService(m_sc_manager, name, dwDesiredAccess); + if (m_sc_service) + return true; + } + + Close(); + return false; +} + +void Service::Close() +{ + if (m_sc_service) + { + ::CloseServiceHandle(m_sc_service); + m_sc_service = NULL; + } + if (m_sc_manager) + { + ::CloseServiceHandle(m_sc_manager); + m_sc_manager = NULL; + } +} + +bool Service::GetStatus(Status *status) const +{ + if (::QueryServiceStatus(m_sc_service, status)) + return true; + else + return false; +} + +bool Service::Start() +{ + if (::StartService(m_sc_service, 0, NULL)) + return true; + else + return false; +} + +bool Service::StartAndWait() +{ + if (Start()) + { + Status status; + return WaitPending(&status, SERVICE_START_PENDING); + } + else + return false; +} + +bool Service::Stop() +{ + Status status; + if (::ControlService(m_sc_service, SERVICE_CONTROL_STOP, &status)) + return true; + else + return false; +} + +bool Service::StopAndWait() +{ + Status status; + if (::ControlService(m_sc_service, SERVICE_CONTROL_STOP, &status)) + { + return WaitPending(&status, SERVICE_STOP_PENDING); + } + else + return false; +} + +bool Service::WaitPending(Status *status, DWORD existing_state) +{ + if (GetStatus(status)) + { + DWORD dwStartTickCount = GetTickCount(); + DWORD dwOldCheckPoint = status->dwCheckPoint; + + while (status->dwCurrentState == existing_state) + { + ATLTRACE(_T("WaitPending\n")); + DWORD dwWaitTime = status->dwWaitHint / 10; + if (dwWaitTime < 1000) + dwWaitTime = 1000; + else if (dwWaitTime > 10000) + dwWaitTime = 10000; + + ::Sleep(dwWaitTime); + + if (!GetStatus(status)) + return false; + + if (status->dwCheckPoint != dwOldCheckPoint) + { + // The service is making progress + dwStartTickCount = GetTickCount(); + dwOldCheckPoint = status->dwCheckPoint; + } + else if (GetTickCount() - dwStartTickCount > status->dwWaitHint) + { + /// Hmm. No progress + return false; + } + } + } + return true; +} + +CString Service::GetBinaryPath() const +{ + CString path; + + DWORD bytes_required; + ::QueryServiceConfig(m_sc_service, NULL, 0, &bytes_required); + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + void *buffer = operator new(bytes_required); + LPQUERY_SERVICE_CONFIG config = reinterpret_cast(buffer); + + if (::QueryServiceConfig(m_sc_service, config, bytes_required, &bytes_required)) + { + path = config->lpBinaryPathName; + } + + delete buffer; + } + return path; +} + +DWORD Service::GetStartup() const +{ + DWORD result = 0xffffffff; + const size_t BUFFER_SIZE = 8192; + void *buffer = operator new(BUFFER_SIZE); + LPQUERY_SERVICE_CONFIG config = reinterpret_cast(buffer); + + DWORD bytes_required; + if (::QueryServiceConfig(m_sc_service, config, BUFFER_SIZE, &bytes_required)) + result = config->dwStartType; + + delete buffer; + return result; +} + +bool Service::ConfigureStartup(DWORD startup) +{ + if (::ChangeServiceConfig(m_sc_service, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) + return true; + else + return false; +} + +void ServiceStatusMonitor::Poll(Service *service) +{ + Service::Status new_status; + if (service->GetStatus(&new_status)) + { + if (!m_service_status.IsValid() || (m_service_status != new_status)) + { + FireServiceStatus(m_service_status, new_status); + m_service_status = new_status; + } + } +} diff --git a/win32/FireflyShell/ServiceControl.h b/win32/FireflyShell/ServiceControl.h new file mode 100644 index 00000000..03ef0476 --- /dev/null +++ b/win32/FireflyShell/ServiceControl.h @@ -0,0 +1,171 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef SERVICECONTROL_H +#define SERVICECONTROL_H + +class Service +{ + SC_HANDLE m_sc_manager; + SC_HANDLE m_sc_service; + bool m_can_control; + +public: + + class Status : public SERVICE_STATUS + { + bool WaitPending(); + + public: + Status() + { + SERVICE_STATUS *clearable_this = this; + ZeroMemory(clearable_this, sizeof(SERVICE_STATUS)); + } + + bool operator==(const Status &r) const + { + const SERVICE_STATUS *lhs = this; + const SERVICE_STATUS *rhs = &r; + return memcmp(lhs, rhs, sizeof(SERVICE_STATUS)) == 0; + } + + bool operator!=(const Status &r) const + { + const SERVICE_STATUS *lhs = this; + const SERVICE_STATUS *rhs = &r; + return memcmp(lhs, rhs, sizeof(SERVICE_STATUS)) != 0; + } + + void AssertValid() const + { + // If we've been written then this shouldn't be zero. + ATLASSERT(dwCurrentState != 0); + } + + bool IsValid() const + { + return dwCurrentState != 0; + } + + bool IsRunning() const + { + AssertValid(); + return dwCurrentState == SERVICE_RUNNING; + } + + bool IsStopped() const + { + AssertValid(); + return dwCurrentState == SERVICE_STOPPED; + } + + bool IsPaused() const + { + AssertValid(); + return dwCurrentState == SERVICE_PAUSED; + } + + bool IsPending() const + { + AssertValid(); + switch (dwCurrentState) + { + case SERVICE_CONTINUE_PENDING: + case SERVICE_PAUSE_PENDING: + case SERVICE_START_PENDING: + case SERVICE_STOP_PENDING: + return true; + default: + return false; + } + } + }; + + Service() : m_sc_manager(NULL), m_sc_service(NULL), m_can_control(false) {} + ~Service() { Close(); } + + bool IsOpen() const + { + return m_sc_service != NULL; + } + bool Open(const TCHAR *name); + void Close(); + + bool GetStatus(Status *status) const; + + bool Start(); + bool StartAndWait(); + bool Stop(); + bool StopAndWait(); + bool WaitPending(Status *status, DWORD existing_state); + bool PollPending(Status *status, DWORD existing_state); + + bool CanControl() const + { + // For the time being - need to deal with running as a user that can't control. + return IsOpen() && m_can_control; + } + + CString GetBinaryPath() const; + + /// Pass SERVICE_AUTO_START, SERVICE_BOOT_START, SERVICE_DEMAND_START, + /// SERVICE_DISABLED or SERVICE_SYSTEM_START. + bool ConfigureStartup(DWORD dwStartup); + + DWORD GetStartup() const; +}; + +class ServiceStatusObserver +{ +public: + virtual void OnServiceStatus(Service::Status old_status, Service::Status new_status) = 0; +}; + +class ServiceStatusMonitor +{ + Service::Status m_service_status; + std::vector m_service_observers; + + void FireServiceStatus(Service::Status old_status, Service::Status new_status) + { + std::vector::iterator i = m_service_observers.begin(); + while (i != m_service_observers.end()) + { + (*i)->OnServiceStatus(old_status, new_status); + ++i; + } + } + +public: + void Poll(Service *service); + void Subscribe(ServiceStatusObserver *obs) + { + m_service_observers.push_back(obs); + } + + void Unsubscribe(ServiceStatusObserver *obs) + { + std::vector::iterator i = std::find(m_service_observers.begin(), m_service_observers.end(), obs); + ATLASSERT(i != m_service_observers.end()); + if (i != m_service_observers.end()) + { + m_service_observers.erase(i); + } + } + +}; +#endif // SERVICECONTROL_H diff --git a/win32/FireflyShell/VersionInfo.cpp b/win32/FireflyShell/VersionInfo.cpp new file mode 100644 index 00000000..d4cab6b5 --- /dev/null +++ b/win32/FireflyShell/VersionInfo.cpp @@ -0,0 +1,111 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" +#include "VersionInfo.h" + +bool VersionInfo::Open(const TCHAR *filename) +{ + Close(); + + DWORD useless; + m_size = GetFileVersionInfoSize(filename, &useless); + if (m_size) + { + m_buffer = operator new(m_size); + ::ZeroMemory(m_buffer, m_size); + if (GetFileVersionInfo(filename, 0, m_size, m_buffer)) + { + return IdentifySubBlock(); + } + else + Close(); + } + return false; +} + +bool VersionInfo::IdentifySubBlock() +{ + m_subblock.Empty(); + WORD required_langid = LANGIDFROMLCID(GetUserDefaultLCID()); + + UINT cbTranslate; + struct LANGANDCODEPAGE + { + WORD wLanguage; + WORD wCodePage; + } *lpTranslate; + + + if (VerQueryValue(m_buffer, _T("\\VarFileInfo\\Translation"), + reinterpret_cast(&lpTranslate), + &cbTranslate)) + { + // Try and find the user's language, but if we can't then just use the + // first one in the file. + int lang_index = -1; + for(unsigned i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++ ) + { + // If we have an exact match then great. + if (lpTranslate[i].wLanguage == required_langid) + { + lang_index = i; + break; + } + // Otherwise settle for a primary language match and keep looking + else if ((PRIMARYLANGID(lpTranslate[i].wLanguage) == PRIMARYLANGID(required_langid)) && (lang_index < 0)) + { + lang_index = i; + } + } + if (lang_index < 0) + { + ATLTRACE("Failed to find a matching language. Just using the first one.\n"); + lang_index = 0; + } + + m_subblock.Format(_T("\\StringFileInfo\\%04x%04x\\"), lpTranslate[lang_index].wLanguage, lpTranslate[lang_index].wCodePage); + return true; + } + return false; +} + +void VersionInfo::Close() +{ + if (m_buffer) + { + delete m_buffer; + m_buffer = NULL; + } +} + +CString VersionInfo::GetString(const TCHAR *name) const +{ + CString path = m_subblock + name; + + LPVOID buffer; + UINT cb; + if (VerQueryValue(m_buffer, const_cast(static_cast(path)), &buffer, &cb)) + { + return CString(static_cast(buffer), cb); + } + return CString(); +} + +//CString VersionInfo::GetStringFileVersion() +//{ +// VerQueryValue(m_buffer, +//} \ No newline at end of file diff --git a/win32/FireflyShell/VersionInfo.h b/win32/FireflyShell/VersionInfo.h new file mode 100644 index 00000000..222cf765 --- /dev/null +++ b/win32/FireflyShell/VersionInfo.h @@ -0,0 +1,46 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef VERSIONINFO_H +#define VERSIONINFO_H + +class VersionInfo +{ + size_t m_size; + void *m_buffer; + CString m_subblock; + + bool IdentifySubBlock(); + + CString GetString(const TCHAR *name) const; + +public: + VersionInfo() : m_size(0), m_buffer(NULL) {} + + bool Open(const TCHAR *filename); + void Close(); + + CString GetFileDescription() const + { + return GetString(_T("FileDescription")); + } + + CString GetFileVersion() const + { + return GetString(_T("FileVersion")); + } +}; +#endif // VERSIONINFO_H diff --git a/win32/FireflyShell/res/FireflyShell.ico b/win32/FireflyShell/res/FireflyShell.ico new file mode 100644 index 0000000000000000000000000000000000000000..bb4df498d87f8d41af512cbb2b1f416370fc96ee GIT binary patch literal 1150 zcmcIjF%H5o47>^+(22PtJ1YYpVQ1$F46J;Hg$?l%CLYiw6+<{TGIBziRv=Pc?bvr0 zS5bkAPhEqn!EO#Lm@Nq@HAL0Ffem|qUt4$xzFC*GhhWHKsLr!a{v5Q{!_HvqnfXfk zQqLX{@yr=fUBAx?(>%k8JdAfL?e`h|n^)>7sl+MuQoY`nop-8Ucna$?Rj;#1R3D9Y z9?R2Qc^b7YQEDT4xvS{D)JNlp-X16UhhIwgVek2;w#5AbTyMmIIngoi+5oo#+zfcK L?)D+~M?kbM5=h4+ literal 0 HcmV?d00001 diff --git a/win32/FireflyShell/res/ShellRunning.ico b/win32/FireflyShell/res/ShellRunning.ico new file mode 100644 index 0000000000000000000000000000000000000000..bb4df498d87f8d41af512cbb2b1f416370fc96ee GIT binary patch literal 1150 zcmcIjF%H5o47>^+(22PtJ1YYpVQ1$F46J;Hg$?l%CLYiw6+<{TGIBziRv=Pc?bvr0 zS5bkAPhEqn!EO#Lm@Nq@HAL0Ffem|qUt4$xzFC*GhhWHKsLr!a{v5Q{!_HvqnfXfk zQqLX{@yr=fUBAx?(>%k8JdAfL?e`h|n^)>7sl+MuQoY`nop-8Ucna$?Rj;#1R3D9Y z9?R2Qc^b7YQEDT4xvS{D)JNlp-X16UhhIwgVek2;w#5AbTyMmIIngoi+5oo#+zfcK L?)D+~M?kbM5=h4+ literal 0 HcmV?d00001 diff --git a/win32/FireflyShell/res/ShellStopped.ico b/win32/FireflyShell/res/ShellStopped.ico new file mode 100644 index 0000000000000000000000000000000000000000..3033762d11a37b073cc13cad2a309b3ecd36f0c6 GIT binary patch literal 1150 zcmcIhF%H5o3_Ju6=)}y(&Ifo7J5OL>?cUcQUc$r!x};(VXGK=yqzMs-R9B~G-`Qz^ z6`!sH-#eT(z!t!sAVntd3^+3P|223(@Xhkn2}&L%9_V}3YD{N{`7~G8fA$V%wRLk3 z%j@}S|0l1p9<%qdORk^mYB{Z{)zosSUglJ!IsIOW(j$g?NNnvN`^H|a`dE{C$sFY+ wcV+J@_G;;Gy>maC|K&@j8HvXexZQ~h_r%2gGzPc>;MTy4ygP@zzlNK510o2{_W%F@ literal 0 HcmV?d00001 diff --git a/win32/FireflyShell/resource.h b/win32/FireflyShell/resource.h new file mode 100644 index 00000000..8853f4d8 --- /dev/null +++ b/win32/FireflyShell/resource.h @@ -0,0 +1,59 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by FireflyShell.rc +// +#define IDR_MAINFRAME 128 +#define IDS_SERVER_RUNNING 129 +#define IDS_SERVER_STOPPED 130 +#define IDS_SERVERSTARTFAIL 131 +#define IDS_SERVERSTOPFAIL 132 +#define IDS_SERVER_PENDING 133 +#define IDS_SERVER_START 134 +#define IDS_SERVER_STOP 135 +#define IDS_SCAN_START 136 +#define IDS_SCAN_STOP 137 +#define IDS_NOT_ADMIN 138 +#define IDS_QUERYSERVERRESTART 139 +#define IDS_VERSIONINFO_DESCRIPTION 140 +#define IDS_VERSIONINFO_VERSION 141 +#define IDS_VERSIONINFO_PATH 142 +#define IDS_FAILED_CONFIGURE_SERVICE 143 +#define IDS_FAILED_CONFIGURE_STARTUP 144 +#define IDD_PAGE_BASIC 201 +#define IDD_PAGE_ADVANCED 202 +#define IDD_PAGE_LOG 203 +#define IDD_PAGE_ABOUT 204 +#define IDI_SHELL_STOPPED 207 +#define IDC_SERVERNAME 1000 +#define IDC_PATH 1001 +#define IDC_BROWSE 1002 +#define IDI_SHELL_RUNNING 1003 +#define IDM_CONTEXT 1003 +#define IDC_LOG 1003 +#define ID_CONFIGURE 1004 +#define IDC_PROTECT 1004 +#define IDC_PASSWORD 1005 +#define IDC_PASSWORD_PROMPT 1006 +#define ID_EXIT 1007 +#define IDC_PORTSPIN 1007 +#define IDC_STARTSERVICE 1008 +#define IDC_SERVERPORT 1009 +#define IDC_STOPSERVICE 1010 +#define IDC_SERVERSTATE 1011 +#define IDC_AUTOSTART 1012 +#define IDC_WEBADMIN 1013 +#define IDC_WEBSITE 1014 +#define IDC_VERSIONLIST 1015 +#define IDC_COPY 1016 +#define ID_SHELLNOTIFY 4242 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 208 +#define _APS_NEXT_COMMAND_VALUE 32773 +#define _APS_NEXT_CONTROL_VALUE 1017 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/win32/FireflyShell/singleton.h b/win32/FireflyShell/singleton.h new file mode 100644 index 00000000..bb6bcc25 --- /dev/null +++ b/win32/FireflyShell/singleton.h @@ -0,0 +1,49 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#ifndef SINGLETON_H +#define SINGLETON_H + +template +class Singleton +{ + static T *sm_instance; + +public: + Singleton() + { + T *instance = static_cast(this); + ATLASSERT(sm_instance == NULL); + sm_instance = instance; + } + + virtual ~Singleton() + { + ATLASSERT(sm_instance != NULL); + sm_instance = NULL; + } + + static T *GetInstance() + { + ATLASSERT(sm_instance != NULL); + return sm_instance; + } +}; + +template +T *Singleton::sm_instance; + +#endif // SINGLETON_H \ No newline at end of file diff --git a/win32/FireflyShell/stdafx.cpp b/win32/FireflyShell/stdafx.cpp new file mode 100644 index 00000000..cfd0a5d3 --- /dev/null +++ b/win32/FireflyShell/stdafx.cpp @@ -0,0 +1,21 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#include "stdafx.h" + +#if (_ATL_VER < 0x0700) +#include +#endif //(_ATL_VER < 0x0700) diff --git a/win32/FireflyShell/stdafx.h b/win32/FireflyShell/stdafx.h new file mode 100644 index 00000000..a2b07feb --- /dev/null +++ b/win32/FireflyShell/stdafx.h @@ -0,0 +1,77 @@ +/* + *(C) 2006 Roku LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Please read README.txt in the same directory as this source file for + * further license information. + */ + +#pragma once + +// Change these values to use different versions +#define WINVER 0x0400 +#define _WIN32_WINNT 0x0501 +#define _WIN32_IE 0x0500 +#define _RICHEDIT_VER 0x0100 + +#define _UNICODE +#define UNICODE + +#include +#include + +extern CAppModule _Module; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// WtsApi32.h is only in the latest platform SDK. Don't rely +// on everyone having it. +#if !defined(NOTIFY_FOR_THIS_SESSION) +#define NOTIFY_FOR_THIS_SESSION 0 +#endif + +#if defined _M_IX86 + #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_IA64 + #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 + #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else + #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif + +// Visual C++ 2005's new secure string operations +#define USE_SECURE 0 +#if defined(_MSC_VER) +#if _MSC_VER >= 1400 +#undef USE_SECURE +#define USE_SECURE 1 +#endif // _MSC_VER >= 1400 +#endif // defined(_MSC_VER) + +inline void SafeStringCopy(TCHAR *dest, const TCHAR *source, size_t len) +{ +#if USE_SECURE + _tcsncpy_s(dest, len, source, len); +#else + _tcsncpy(dest, source, len); + dest[len - 1] = 0; +#endif +} \ No newline at end of file diff --git a/win32/FireflyShell/version.h.templ b/win32/FireflyShell/version.h.templ new file mode 100644 index 00000000..e21c9219 --- /dev/null +++ b/win32/FireflyShell/version.h.templ @@ -0,0 +1,13 @@ +/* Version number of package */ +#define VERSION_MAJOR 0 +#define VERSION_MID 1 +#define VERSION_MINOR 0 +#define VERSION_BUILD $WCREV$ + +#define VERSION_LOCAL "$WCMODS? (LOCAL):$" +#define BUILD_DATE "$WCNOW$" +#define REPO_DATE "$WCDATE$" + +#define VERSION_STRIZE2(x) #x +#define VERSION_STRIZE(x) VERSION_STRIZE2(x) +#define VERSION_STRING VERSION_STRIZE(VERSION_MAJOR) "." VERSION_STRIZE(VERSION_MID) "-svn" VERSION_STRIZE(VERSION_BUILD) VERSION_LOCAL diff --git a/win32/FireflyShell/version.rc b/win32/FireflyShell/version.rc new file mode 100644 index 00000000..6e57b2ac --- /dev/null +++ b/win32/FireflyShell/version.rc @@ -0,0 +1,44 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "version.h" +#include "winver.h" + +#ifndef APSTUDIO_INVOKED + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_MAJOR, VERSION_MID, VERSION_MINOR, VERSION_BUILD + PRODUCTVERSION VERSION_MAJOR, VERSION_MID, VERSION_MINOR, VERSION_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Roku LLC" + VALUE "FileDescription", "FireflyShell" + VALUE "FileVersion", VERSION_STRING + VALUE "InternalName", "FireflyShell" + VALUE "LegalCopyright", "Copyright 2006 Roku LLC" + VALUE "OriginalFilename", "FireflyShell.exe" + VALUE "ProductName", "Firefly Media Server" + VALUE "ProductVersion", VERSION_STRING + VALUE "Comments", "Build date: " BUILD_DATE "\r\nRepository date: " REPO_DATE "\r\n" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif //APSTUDIO_INVOKED diff --git a/win32/versionize.bat b/win32/versionize.bat index e7fa3112..21c7c543 100644 --- a/win32/versionize.bat +++ b/win32/versionize.bat @@ -8,6 +8,7 @@ echo Fixing version info... %SUBWC% %0\..\.. %0\..\FireflyConfig\AssemblyInfo.cs.templ %0\..\FireflyConfig\AssemblyInfo.cs %SUBWC% %0\..\.. %0\..\nsi\mt-daapd.nsi.templ %0\..\nsi\mt-daapd.nsi %SUBWC% %0\..\.. %0\..\ssc-ffmpeg.rc.templ %0\..\ssc-ffmpeg.rc +%SUBWC% %0\..\.. %0\..\FireflyShell\version.h.templ %0\..\FireflyShell\version.h goto END :NOSUBWC @@ -15,4 +16,5 @@ copy %0\..\config.h.templ %0\..\config.h copy %0\..\FireflyConfig\AssemblyInfo.cs.templ %0\..\FireflyConfig\AssemblyInfo.cs copy %0\..\nsi\mt-daapd.nsi.templ %0\..\nsi\mt-daapd.nsi copy %0\..\.. %0\..\ssc-ffmpeg.rc.templ %0\..\ssc-ffmpeg.rc +copy %0\..\FireflyShell\version.h.templ %0\..\version.h :END