sbWindowMoveService.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2009 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 #include "sbWindowMoveService.h"
26 #include "../NativeWindowFromNode.h"
27 
28 #include <nsComponentManagerUtils.h>
29 #include <nsThreadUtils.h>
30 
31 #define PROP_WMS_INST L"WindowMoveServiceInstance"
32 
33 static const PRUint32 DEFAULT_HASHTABLE_SIZE = 1;
34 
36 
38 {
39 }
40 
42 {
43 }
44 
45 nsresult
47 {
48  return NS_OK;
49 }
50 
51 inline HHOOK
53 {
54  sbWindowMoveService::hooks_t::const_iterator cit = aHooks.find(aWnd);
55  if(cit != aHooks.end()) {
56  return cit->second;
57  }
58 
59  return NULL;
60 }
61 
62 inline void
64  const sbWindowMoveService::listeners_t &aListeners)
65 {
66  sbWindowMoveService::listeners_t::const_iterator cit = aListeners.find(aWnd);
67  if(cit != aListeners.end()) {
68  cit->second->OnMoveStarted();
69  }
70 
71  return;
72 }
73 
74 inline void
76  const sbWindowMoveService::listeners_t &aListeners)
77 {
78  sbWindowMoveService::listeners_t::const_iterator cit = aListeners.find(aWnd);
79  if(cit != aListeners.end()) {
80  cit->second->OnMoveStopped();
81  }
82 
83  return;
84 }
85 
86 /*static*/ LRESULT CALLBACK
87 sbWindowMoveService::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
88 {
89  // This is a message we shouldn't process. See SetWindowHookEx docs
90  // on MSDN for more information about how to process window hook
91  // messages.
92  if(nCode < 0) {
93  return CallNextHookEx(NULL, nCode, wParam, lParam);
94  }
95 
96  PCWPSTRUCT msg = reinterpret_cast<PCWPSTRUCT>(lParam);
97 
98  sbWindowMoveService *self =
99  reinterpret_cast<sbWindowMoveService *>(::GetPropW(msg->hwnd, PROP_WMS_INST));
100  if (!self) {
101  // This wasn't a window that we're interested in, don't do anything
102  return CallNextHookEx(NULL, nCode, wParam, lParam);
103  }
104 
105  if(msg->message == WM_WINDOWPOSCHANGING) {
106  // if the window didn't actually move, then we don't actually care
107  WINDOWINFO wi;
108  wi.cbSize = sizeof(WINDOWINFO);
109  if (GetWindowInfo(msg->hwnd, &wi)) {
110  RECT* oldpos = &wi.rcWindow;
111  WINDOWPOS* newpos = (WINDOWPOS*)msg->lParam;
112  if (newpos->flags & SWP_NOMOVE) {
113  return CallNextHookEx(NULL, nCode, wParam, lParam);
114  }
115  }
116 
117  sbWindowMoveService::resizing_t::iterator it =
118  self->mResizing.find(msg->hwnd);
119 
120  if(it == self->mResizing.end()) {
121  self->mResizing.insert(std::make_pair<HWND, bool>(msg->hwnd, true));
122  CallListenerMoveStarted(msg->hwnd, self->mListeners);
123  }
124  else if(!it->second) {
125  it->second = true;
126  CallListenerMoveStarted(msg->hwnd, self->mListeners);
127  }
128  }
129  else if(msg->message == WM_WINDOWPOSCHANGED) {
130  sbWindowMoveService::resizing_t::iterator it =
131  self->mResizing.find(msg->hwnd);
132 
133  if(it != self->mResizing.end() &&
134  it->second == true) {
135 
136  nsCOMPtr<nsITimer> timer;
137  timers_t::iterator itTimer = self->mTimers.find(msg->hwnd);
138  if(itTimer == self->mTimers.end()) {
139  nsresult rv = NS_ERROR_UNEXPECTED;
140 
141  timer = do_CreateInstance("@mozilla.org/timer;1");
142  self->mTimers.insert(
143  std::make_pair<HWND, nsCOMPtr<nsITimer> >(msg->hwnd, timer));
144  }
145  else {
146  timer = itTimer->second;
147  }
148 
149  if(timer) {
150  self->mTimersToWnd.insert(
151  std::make_pair<nsITimer*, HWND>(timer.get(), msg->hwnd));
152  timer->InitWithCallback(self, 1000, nsITimer::TYPE_ONE_SHOT);
153  }
154  }
155  }
156 
157  return CallNextHookEx(NULL, nCode, wParam, lParam);
158 }
159 
160 PRBool
162 {
163  NS_ENSURE_ARG_POINTER(aWnd);
164 
165  if(mHooks.find(aWnd) != mHooks.end()) {
166  return PR_TRUE;
167  }
168 
169  return PR_FALSE;
170 }
171 
172 NS_IMETHODIMP
173 sbWindowMoveService::StartWatchingWindow(nsISupports *aWindow,
174  sbIWindowMoveListener *aListener)
175 {
176  NS_ENSURE_ARG_POINTER(aWindow);
177  NS_ENSURE_ARG_POINTER(aListener);
178 
179  NS_WARN_IF_FALSE(NS_IsMainThread(), "This service is MAIN THREAD ONLY!");
180 
181  HWND windowHandle = NULL;
182 
183  windowHandle = NativeWindowFromNode::get(aWindow);
184  NS_ENSURE_TRUE(windowHandle, NS_ERROR_INVALID_ARG);
185 
186  // Already hooked. Can only hook once.
187  if(IsHooked(windowHandle)) {
188  NS_WARNING("Window already hooked. Can only hook a window once.");
189  return NS_OK;
190  }
191 
192  BOOL success = ::SetPropW(windowHandle, PROP_WMS_INST, (HANDLE) this);
193  NS_ENSURE_TRUE(success != 0, NS_ERROR_UNEXPECTED);
194 
195  HHOOK hookHandle = ::SetWindowsHookEx(WH_CALLWNDPROC,
197  NULL,
198  ::GetCurrentThreadId());
199  NS_ENSURE_TRUE(hookHandle, NS_ERROR_FAILURE);
200 
201  nsCOMPtr<sbIWindowMoveListener> listener(aListener);
202  mListeners.insert(
203  std::make_pair<HWND, nsCOMPtr<sbIWindowMoveListener> >(windowHandle,
204  listener));
205 
206  mHooks.insert(std::make_pair<HWND, HHOOK>(windowHandle, hookHandle));
207 
208  return NS_OK;
209 }
210 
211 NS_IMETHODIMP
212 sbWindowMoveService::StopWatchingWindow(nsISupports *aWindow,
213  sbIWindowMoveListener *aListener)
214 {
215  NS_ENSURE_ARG_POINTER(aWindow);
216  NS_ENSURE_ARG_POINTER(aListener);
217 
218  NS_WARN_IF_FALSE(NS_IsMainThread(), "This service is MAIN THREAD ONLY!");
219 
220  HWND windowHandle = NativeWindowFromNode::get(aWindow);
221  NS_ENSURE_TRUE(windowHandle, NS_ERROR_INVALID_ARG);
222 
223  // Not hooked, nothing to do.
224  if(!IsHooked(windowHandle)) {
225  NS_WARNING("Attempting to unhook a window that was never hooked.");
226  return NS_OK;
227  }
228 
229  HANDLE propHandle = ::RemovePropW(windowHandle, PROP_WMS_INST);
230  NS_WARN_IF_FALSE(propHandle == (HANDLE) this,
231  "Removed property that didn't match what we should've set!");
232 
233  HHOOK hookHandle = GetHookForWindow(windowHandle, mHooks);
234  NS_ENSURE_TRUE(hookHandle, NS_ERROR_FAILURE);
235 
236  BOOL success = ::UnhookWindowsHookEx(hookHandle);
237  NS_ENSURE_TRUE(success != 0, NS_ERROR_FAILURE);
238 
239  mHooks.erase(windowHandle);
240  mListeners.erase(windowHandle);
241 
242  return NS_OK;
243 }
244 
245 NS_IMETHODIMP
246 sbWindowMoveService::Notify(nsITimer *aTimer)
247 {
248  timertohwnd_t::iterator it = mTimersToWnd.find(aTimer);
249 
250  if(it == mTimersToWnd.end()) {
251  return NS_OK;
252  }
253 
254  HWND window = it->second;
255 
257 
258  nsCOMPtr<nsITimer> grip(aTimer);
259 
260  mResizing.erase(window);
261  mTimers.erase(window);
262  mTimersToWnd.erase(aTimer);
263 
264  return NS_OK;
265 }
return NS_OK
NS_DECL_ISUPPORTS NS_DECL_SBIWINDOWMOVESERVICE NS_DECL_NSITIMERCALLBACK typedef std::map< HWND, nsCOMPtr< sbIWindowMoveListener > > listeners_t
void CallListenerMoveStarted(HWND aWnd, const sbWindowMoveService::listeners_t &aListeners)
PRBool IsHooked(HWND aWnd)
static const PRUint32 DEFAULT_HASHTABLE_SIZE
NS_IMPL_ISUPPORTS2(sbAlbumArtService, sbIAlbumArtService, nsIObserver) NS_IMETHODIMP sbAlbumArtService
timertohwnd_t mTimersToWnd
let window
std::map< HWND, HHOOK > hooks_t
Native window manager interface implementation.
static void * get(nsISupports *window)
#define PROP_WMS_INST
var self
if(DEBUG_DATAREMOTES)
void CallListenerMoveStopped(HWND aWnd, const sbWindowMoveService::listeners_t &aListeners)
function msg
HHOOK GetHookForWindow(HWND aWnd, const sbWindowMoveService::hooks_t &aHooks)
nsITimerCallback
static LRESULT CALLBACK CallWndProc(int aCode, WPARAM wParam, LPARAM lParam)