feisty meow concerns codebase  2.140
process_anchor.cpp
Go to the documentation of this file.
1 /*
2 *
3 * Name : process_anchor
4 * Author : Chris Koeritz
5 *
6 ****
7 * Copyright (c) 2000-$now By Author. This program is free software; you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; either version 2 of
10 * the License or (at your option) any later version. This is online at:
11 * http://www.fsf.org/copyleft/gpl.html
12 * Please send any updates to: fred@gruntose.com
13 */
14 
15 #include "process_anchor.h"
16 
18 #include <basis/array.h>
19 #include <basis/definitions.h>
20 #include <basis/utf_conversion.h>
21 #include <basis/astring.h>
22 #include <filesystem/filename.h>
25 
26 using namespace application;
27 using namespace basis;
28 using namespace filesystem;
29 using namespace loggers;
30 using namespace structures;
31 //using namespace timely;
32 
33 #undef LOG
34 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
35 
36 #ifdef __WIN32__
37 const int STARTUP_MESSAGE = WM_USER + 123;
38  // the special message we send out on startup of the event loop.
39 
40 const int TIMER_EVENT_ID = 23;
41  // the ID we use to create a timer, if the user wants one.
42 
43 // an anchor_association tracks the object related to a window handle.
44 struct anchor_association {
45  window_handle _handle;
46  process_anchor *_anchor_object;
47 
48  anchor_association(window_handle wh = NULL_POINTER, process_anchor *wo = NULL_POINTER)
49  : _handle(wh), _anchor_object(wo) {}
50 };
51 
52 class anchor_association_list
53 : public array<anchor_association>, public virtual root_object
54 {
55 public:
56  DEFINE_CLASS_NAME("anchor_association_list");
57 };
58 
59 // the associations list keeps track of anchors that have been created so
60 // that the window procedure (on win32) can get to the real object.
61 
62 SAFE_STATIC(anchor_association_list, associations, )
63 #endif //win32
64 
66 
68 #ifdef __LINUX__
70 #endif
71 #ifdef __WIN32__
72 : _instance(NULL_POINTER),
73  _anchor_title(new astring),
74  _anchor_class(new astring),
75  _class_reg(NULL_POINTER),
76  _wind_handle(NULL_POINTER),
77  _cycle(0),
78  _defunct(false)
79 #endif
80 {
81 }
82 
84 {
85  set_defunct();
86 #ifdef __WIN32__
87  WHACK(_anchor_title);
88  WHACK(_anchor_class);
89  _class_reg = NULL_POINTER;
90  // remove the association for our anchor.
91  for (int i = 0; i < associations().length(); i++) {
92  if (associations()[i]._handle == _wind_handle) {
93  associations().zap(i, i);
94  break;
95  }
96  }
97 #endif
98 }
99 
100 void process_anchor::handle_startup() { /* nothing for base class. */ }
101 
102 void process_anchor::handle_timer() { /* nothing for base class. */ }
103 
104 void process_anchor::handle_shutdown() { /* nothing for base class. */ }
105 
107 {
108 #ifdef __LINUX__
110  return true;
111 #endif
112 #ifdef __WIN32__
113  bool to_return = false;
114  for (int i = 0; i < associations().length(); i++) {
115  window_handle win = associations()[i]._handle;
116  int ret = PostMessage(win, WM_CLOSE, NULL_POINTER, NULL_POINTER);
117  // if we got a healthy return from any post, then we'll say this worked.
118  if (ret) to_return = true;
119  }
120  return to_return;
121 #endif
122 }
123 
125 {
126 #ifdef __LINUX__
128 #endif
129 #ifdef __WIN32__
130  return _defunct;
131 #endif
132 }
133 
135 {
136 #ifdef __LINUX__
138 #endif
139 #ifdef __WIN32__
140  _defunct = true;
141 #endif
142 }
143 
145 {
146  FUNCDEF("close_app_anchor");
147 #ifdef __LINUX__
148  return shutdown_alerter::close_application(app_name);
149 #endif
150 #ifdef __WIN32__
152 #ifdef DEBUG_PROCESS_MANAGER
153  LOG(astring("title is: ") + title);
154 #endif
155 
156 //hmmm: add support for linux...
157 #ifdef __WIN32__
158  window_handle win_found = FindWindow(NULL_POINTER, to_unicode_temp(title));
159 #ifdef DEBUG_PROCESS_MANAGER
160  LOG(astring(astring::SPRINTF, "found window %lx", win_found));
161 #endif
162  if (!win_found) {
163  LOG(astring("Failed to find special window for [") + app_name
164  + astring("]"));
165  return false;
166  }
167 
168 //hmmm:
169 //why would we find only one window if there were multiple apps under that
170 //name? how does windows deal with that? is there a find window iterator?
171 
172  int ret = PostMessage(win_found, WM_CLOSE, NULL_POINTER, NULL_POINTER);
173  if (!ret) {
174  LOG(astring("Failed to send close message to [") + app_name
175  + astring("]"));
176  return false;
177  }
178 
179  LOG(astring("Sent close message to [") + app_name + astring("]"));
180 #else
181  #ifdef DEBUG_PROCESS_MANAGER
182  LOG("graceful shutdown not implemented for this OS.");
183  #endif
184  return false;
185 #endif
186  return true;
187 #endif //win32
188 }
189 
191  const astring &app_name, int cycle)
192 {
193 #ifdef __LINUX__
194  if (instance) {}
195  return shutdown_alerter::setup(app_name, cycle);
196 #endif
197 #ifdef __WIN32__
198  if (_wind_handle) return true; // already initialized.
199  // simple initializations first...
200  _instance = instance;
201  _cycle = cycle;
202  *_anchor_title = make_well_known_title(app_name);
203  *_anchor_class = make_well_known_class(app_name);
204  _class_reg = register_class(); // register a new window class for this.
205  _wind_handle = CreateWindow(to_unicode_temp(*_anchor_class),
206  to_unicode_temp(*_anchor_title), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
207  0, CW_USEDEFAULT, 0, NULL_POINTER, NULL_POINTER, _instance, NULL_POINTER);
208  if (!_wind_handle) return false;
209 
210  register_anchor(_wind_handle); // hook in our anchor to the list.
211  ShowWindow(_wind_handle, SW_HIDE);
212  UpdateWindow(_wind_handle);
213  PostMessage(_wind_handle, STARTUP_MESSAGE, 0, 0);
214 #endif //win32
215  return true;
216 }
217 
219 {
220 #ifdef __WIN32__
221  // add the new anchor to our list of associations.
222  associations() += anchor_association(wind, this);
223 #else
224  if (wind) {}
225 #endif
226 }
227 
228 #ifdef __WIN32__
229 ATOM process_anchor::register_class()
230 {
231  WNDCLASSEX wcex;
232 
233  wcex.cbSize = sizeof(WNDCLASSEX);
234 
235  wcex.style = CS_HREDRAW | CS_VREDRAW;
236  wcex.lpfnWndProc = (WNDPROC)WndProc;
237  wcex.cbClsExtra = 0;
238  wcex.cbWndExtra = 0;
239  wcex.hInstance = _instance;
240  wcex.hIcon = NULL_POINTER;
241  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
242  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
243  wcex.lpszMenuName = NULL_POINTER;
244  to_unicode_persist(hold_class, _anchor_class->s());
245  wcex.lpszClassName = hold_class;
246  wcex.hIconSm = NULL_POINTER;
247 
248  return RegisterClassEx(&wcex);
249 }
250 #endif
251 
252 #ifdef __WIN32__
253 LRESULT CALLBACK process_anchor::WndProc(HWND hWnd, UINT message,
254  WPARAM wParam, LPARAM lParam)
255 {
256  switch (message) {
257  case STARTUP_MESSAGE: {
258  for (int i = 0; i < associations().length(); i++) {
259  if (associations()[i]._handle == hWnd) {
260  process_anchor &anch = *associations()[i]._anchor_object;
261  // invoke the initial callback so the anchor can initialize.
262  anch.handle_startup();
263 
264  // set a timer going if they wanted one.
265  if (anch._cycle)
266  SetTimer(anch._wind_handle, TIMER_EVENT_ID, anch._cycle, 0);
267  break;
268  }
269  }
270  break;
271  }
272  case WM_CLOSE: {
273  for (int i = 0; i < associations().length(); i++) {
274  if (associations()[i]._handle == hWnd) {
275  // invoke the closing callback because we're leaving.
276  associations()[i]._anchor_object->handle_shutdown();
277  associations()[i]._anchor_object->set_defunct();
278  break;
279  }
280  }
281  return DefWindowProc(hWnd, message, wParam, lParam);
282  break;
283  }
284  case WM_DESTROY: {
285  PostQuitMessage(0);
286  break;
287  }
288  case WM_TIMER: {
289  bool found_window = false; // records if we dispatched the event.
290  for (int i = 0; i < associations().length(); i++)
291  if (associations()[i]._handle == hWnd) {
292  // always stop the timer since we're suspending time while we're busy.
293  // more of a desire than a strategy.
294  KillTimer(hWnd, TIMER_EVENT_ID);
295  // invoke the timer callback to give the user some action.
296  associations()[i]._anchor_object->handle_timer();
297  found_window = true;
298  // always set the timer going again after handling it.
299  SetTimer(hWnd, TIMER_EVENT_ID,
300  associations()[i]._anchor_object->_cycle, 0);
301  break;
302  }
303  bool to_return = 0;
304  // if the timer wasn't for one of our anchors, we pass it to the default
305  // window procedure in hopes it will know what the hell to do with it.
306  if (!found_anchor)
307  to_return = DefWindowProc(hWnd, message, wParam, lParam);
308  return to_return;
309  }
310  case WM_PAINT: {
311  PAINTSTRUCT ps;
312  HDC hdc = BeginPaint(hWnd, &ps);
313  // add drawing code here if needed.
314  EndPaint(hWnd, &ps);
315  break;
316  }
317  default: return DefWindowProc(hWnd, message, wParam, lParam);
318  }
319  return 0;
320 }
321 #endif
322 
323 #ifdef __WIN32__
325 {
326  filename app_short = application_name;
327  return astring("Anchor_for_") + app_short.basename();
328 }
329 #endif
330 
331 #ifdef __WIN32__
333 {
334  filename app_short = application_name;
335  return astring("Dozeclass_for_") + app_short.basename();
336 }
337 #endif
338 
340  const astring &application_name, int cycle)
341 {
342  // prepare the anchor for its role...
343  if (!win.setup(handle, application_name, cycle)) return false;
344 #ifdef __LINUX__
345  return shutdown_alerter::launch_console(win, application_name, cycle);
346 #endif
347 #ifdef __WIN32__
348  MSG msg;
349  msg.hwnd = 0; msg.message = 0; msg.wParam = 0; msg.lParam = 0;
350  while (GetMessage(&msg, NULL_POINTER, 0, 0)) {
351  TranslateMessage(&msg);
352  DispatchMessage(&msg);
353  }
354 #endif
355  return true;
356 }
357 
358 
359 
360 
Represents a sequential, ordered, contiguous collection of objects.
Definition: array.h:54
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
Provides operations commonly needed on file names.
Definition: filename.h:64
filename basename() const
returns the base of the filename; no directory.
Definition: filename.cpp:385
Implements a graceful shutdown procedure for an application.
void set_defunct()
used by the derived class to mark that this object is about to exit.
static bool close_this_program()
causes this particular program to shut down.
bool setup(application_instance handle, const basis::astring &application_name, int timing_cycle=0)
constructs a process_anchor for the "application_name" specified.
virtual void handle_startup()
derived classes can override this to catch the application startup.
virtual void handle_timer()
invoked periodically if the anchor was setup() with a timer "cycle".
static basis::astring make_well_known_class(const basis::astring &application_name)
same as above but for the anchor's class name.
void register_anchor(window_handle anchor)
this supports the anchor being created elsewhere.
static bool launch(process_anchor &anchor, application_instance handle, const basis::astring &app, int cycle=0)
establishes a process_anchor for the program named "app".
process_anchor()
constructor does very little; setup() begins operation.
static bool close_app_anchor(const basis::astring &app_name)
closes the anchor object associated with "app_name".
bool defunct() const
returns true if the object has been marked as defunct.
static basis::astring make_well_known_title(const basis::astring &application_name)
returns the string form of the well-known window title for the process_anchor.
virtual void handle_shutdown()
invoked just prior to the shutdown of this anchor.
virtual ~process_anchor()
A platform-independent way to alert a program that it should shut down immediately.
static bool is_defunct()
returns true if the object has been marked as defunct.
static void set_defunct()
used by the derived class to mark that this object is about to exit.
bool setup(const basis::astring &app_name, int timer_period=0)
constructs a shutdown_alerter for the "app_name" specified.
static void close_this_program()
causes this particular application to begin shutting down.
static bool launch_console(shutdown_alerter &alert, const basis::astring &app_name, int timer_period=0)
this is used to begin execution of a console mode application.
static bool close_application(const basis::astring &app_name)
attempts to close the application named "app_name".
Constants and objects used throughout HOOPLE.
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:54
Implements an application lock to ensure only one is running at once.
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#define LOG(s)
#define SAFE_STATIC(type, func_name, parms)
Statically defines a singleton object whose scope is the program's lifetime.
Support for unicode builds.
void * ATOM
void * application_instance
void * window_handle
void * HDC