source: trunk/src/user32/oslibmsgtranslate.cpp@ 7211

Last change on this file since 7211 was 7211, checked in by phaller, 24 years ago

.

File size: 39.9 KB
Line 
1/* $Id: oslibmsgtranslate.cpp,v 1.69 2001-10-26 09:10:12 phaller Exp $ */
2/*
3 * Window message translation functions for OS/2
4 *
5 *
6 * Copyright 1998-1999 Sander van Leeuwen (sandervl@xs4all.nl)
7 * Copyright 1999 Daniela Engert (dani@ngrt.de)
8 * Copyright 1999 Rene Pronk (R.Pronk@twi.tudelft.nl)
9 *
10 * Project Odin Software License can be found in LICENSE.TXT
11 *
12 * TODO: Extra msgs: which messages must be put into the queue and which can be sent directly?
13 * (According to the docs TranslateMessage really inserts a msg in the queue)
14 * TODO: Filter translation isn't correct for posted messages
15 *
16 */
17#define INCL_WIN
18#define INCL_PM
19#define INCL_DOSPROCESS
20#include <os2wrap.h>
21#include <string.h>
22#include <misc.h>
23#include <winconst.h>
24#include <win32api.h>
25#include "oslibmsg.h"
26#include <winuser32.h>
27#include "win32wdesktop.h"
28#include "oslibutil.h"
29#include "timer.h"
30#include <thread.h>
31#include <wprocess.h>
32#include "pmwindow.h"
33#include "oslibwin.h"
34#include "winmouse.h"
35#include <pmkbdhk.h>
36#include <pmscan.h>
37#include <winscan.h>
38
39#define DBG_LOCALLOG DBG_oslibmsgtranslate
40#include "dbglocal.h"
41
42// Formerly used method of translation based on OS/2 VKEY value didn't work
43// right. We need to take a look at the scan code we get from PM and derive
44// the Win32 VKEY and scancode from that because sometimes even the scancode
45// used in Win32 is different from the PM scancode!
46// The format is:
47// Win VKEY, Win scancode, (PM scancode) (key description)
48USHORT pmscan2winkey [][2] = {
49 0, 0, // 0x00
50 0x1B, 0x01, // 0x01 Esc
51 0x31, 0x02, // 0x02 1
52 0x32, 0x03, // 0x03 2
53 0x33, 0x04, // 0x04 3
54 0x34, 0x05, // 0x05 4
55 0x35, 0x06, // 0x06 5
56 0x36, 0x07, // 0x07 6
57 0x37, 0x08, // 0x08 7
58 0x38, 0x09, // 0x09 8
59 0x39, 0x0A, // 0x0A 9
60 0x30, 0x0B, // 0x0B 0
61 0xBD, 0x0C, // 0x0C -
62 0xBB, 0x0D, // 0x0D =
63 0x08, 0x0E, // 0x0E Bksp
64 0x09, 0x0F, // 0x0F Tab
65 0x51, 0x10, // 0x10 q
66 0x57, 0x11, // 0x11 w
67 0x45, 0x12, // 0x12 e
68 0x52, 0x13, // 0x13 r
69 0x54, 0x14, // 0x14 t
70 0x59, 0x15, // 0x15 y
71 0x55, 0x16, // 0x16 u
72 0x49, 0x17, // 0x17 i
73 0x4F, 0x18, // 0x18 o
74 0x50, 0x19, // 0x19 p
75 0xDB, 0x1A, // 0x1A [
76 0xDD, 0x1B, // 0x1B ]
77 0x0D, 0x1C, // 0x1C Enter
78 0x11, 0x1D, // 0x1D LCtrl
79 0x41, 0x1E, // 0x1E a
80 0x53, 0x1F, // 0x1F s
81 0x44, 0x20, // 0x20 d
82 0x46, 0x21, // 0x21 f
83 0x47, 0x22, // 0x22 g
84 0x48, 0x23, // 0x23 h
85 0x4A, 0x24, // 0x24 j
86 0x4B, 0x25, // 0x25 k
87 0x4C, 0x26, // 0x26 l
88 0xBA, 0x27, // 0x27 ;
89 0xDE, 0x28, // 0x28 '
90 0xC0, 0x29, // 0x29 `
91 0x10, 0x2A, // 0x2A LShift
92 0xDC, 0x2B, // 0x2B Bkslsh
93 0x5A, 0x2C, // 0x2C z
94 0x58, 0x2D, // 0x2D x
95 0x43, 0x2E, // 0x2E c
96 0x56, 0x2F, // 0x2F v
97 0x42, 0x30, // 0x30 b
98 0x4E, 0x31, // 0x31 n
99 0x4D, 0x32, // 0x32 m
100 0xBC, 0x33, // 0x33 ,
101 0xBE, 0x34, // 0x34 .
102 0xBF, 0x35, // 0x35 /
103 0x10, 0x36, // 0x36 RShift
104 0x6A, 0x37, // 0x37 * Pad
105// 0x12, 0x38, // 0x38 LAlt
106 VK_LMENU_W, 0x38, // 0x38 LAlt
107 0x20, 0x39, // 0x39 Space
108 0x14, 0x3A, // 0x3A CapsLk
109 0x70, 0x3B, // 0x3B F1
110 0x71, 0x3C, // 0x3C F2
111 0x72, 0x3D, // 0x3D F3
112 0x73, 0x3E, // 0x3E F4
113 0x74, 0x3F, // 0x3F F5
114 0x75, 0x40, // 0x40 F6
115 0x76, 0x41, // 0x41 F7
116 0x77, 0x42, // 0x42 F8
117 0x78, 0x43, // 0x43 F9
118 0x79, 0x44, // 0x44 F10 (?)
119 0x90, 0x145, // 0x45 NumLk
120 0x91, 0x46, // 0x46 ScrLk
121 0x24, 0x47, // 0x47 7 Pad
122 0x26, 0x48, // 0x48 8 Pad
123 0x21, 0x49, // 0x49 9 Pad
124 0x6D, 0x4A, // 0x4A - Pad
125 VK_LEFT_W, 0x4B, // 0x4B Left Arrow (depends on NumLock State)
126 0x0C, 0x4C, // 0x4C 5 Pad
127 0x27, 0x4D, // 0x4D 6 Pad
128 0x6B, 0x4E, // 0x4E + Pad
129 0x23, 0x4F, // 0x4F 1 Pad
130 0x28, 0x50, // 0x50 2 Pad
131 0x22, 0x51, // 0x51 3 Pad
132 0x2D, 0x52, // 0x52 0 Pad
133 0x2E, 0x53, // 0x53 . Pad
134 0, 0, // 0x54
135 0, 0, // 0x55
136 0xe2, 0x56, // 0x56 "<>|"
137 0x7A, 0x57, // 0x57 F11
138 0x7B, 0x58, // 0x58 F12
139 0, 0, // 0x59
140 0x0D, 0x11C, // 0x5A Enter Pad
141 0x11, 0x11D, // 0x5B RCtrl
142 0x6F, 0x135, // 0x5C / Pad
143 VK_SNAPSHOT_W, 0x137, // 0x5D PrtSc
144 0x12, 0x5E, // 0x5E RAlt
145 0x13, 0x45, // 0x5F Pause
146 VK_HOME_W, 0x60, // 0x60
147 VK_UP_W, 0x61, // 0x61
148 VK_PRIOR_W, 0x62, // 0x62
149 VK_LEFT_W, 0x63, // 0x63
150 VK_RIGHT_W, 0x64, // 0x64
151 VK_END_W, 0x65, // 0x65
152 VK_DOWN_W, 0x66, // 0x66
153 VK_NEXT_W, 0x67, // 0x67
154 VK_INSERT_W, 0x68, // 0x68
155 VK_DELETE_W, 0x69, // 0x69
156 VK_F23_W, 0x6A, // 0x6A
157 VK_F24_W, 0x6B, // 0x6B
158 0x5D, 0x15D, // 0x6C RWin (PM scan 0x7C)
159 0, 0, // 0x6D
160 0x5B, 0x15B, // 0x6E LWin (PM scan 0x7E)
161 0x5C, 0x15C // 0x6F RMenu? (PM scan 0x7F)
162};
163
164static BOOL fGenerateDoubleClick = FALSE;
165static MSG doubleClickMsg = {0};
166
167
168// Note:
169// For a "lonekey"-press of AltGr, we only receive WM_KEYUP
170// messages. If the key is pressed longer and starts to repeat,
171// WM_KEYDOWN messages come in properly.
172static BOOL fKeyAltGrDown = FALSE;
173
174//******************************************************************************
175//******************************************************************************
176BOOL setThreadQueueExtraCharMessage(TEB* teb, MSG* pExtraMsg)
177{
178 // check if the single slot is occupied already
179 if (teb->o.odin.fTranslated == TRUE)
180 // there's still an already translated message to be processed
181 return FALSE;
182
183 teb->o.odin.fTranslated = TRUE;
184 memcpy(&teb->o.odin.msgWCHAR, pExtraMsg, sizeof(MSG));
185 return TRUE;
186}
187
188//******************************************************************************
189//******************************************************************************
190ULONG GetMouseKeyState()
191{
192 ULONG keystate = 0;
193
194 if(WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000)
195 keystate |= MK_LBUTTON_W;
196 if(WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000)
197 keystate |= MK_RBUTTON_W;
198 if(WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000)
199 keystate |= MK_MBUTTON_W;
200 if(WinGetKeyState(HWND_DESKTOP, VK_SHIFT) & 0x8000)
201 keystate |= MK_SHIFT_W;
202 if(WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)
203 keystate |= MK_CONTROL_W;
204
205 return keystate;
206}
207//******************************************************************************
208//******************************************************************************
209LONG IsNCMouseMsg(Win32BaseWindow *win32wnd)
210{
211 return ((win32wnd->getLastHitTestVal() != HTCLIENT_W) && (WinQueryCapture(HWND_DESKTOP) != win32wnd->getOS2WindowHandle()));
212}
213//******************************************************************************
214//******************************************************************************
215void SetMenuDoubleClick(BOOL fSet)
216{
217 fGenerateDoubleClick = fSet;
218}
219//******************************************************************************
220//******************************************************************************
221BOOL OS2ToWinMsgTranslate(void *pTeb, QMSG *os2Msg, MSG *winMsg, BOOL isUnicode, BOOL fMsgRemoved)
222{
223 Win32BaseWindow *win32wnd = 0;
224 OSLIBPOINT point, ClientPoint;
225 POSTMSG_PACKET *packet;
226 TEB *teb = (TEB *)pTeb;
227 BOOL fWasDisabled = FALSE;
228 BOOL fIsFrame = FALSE;
229 int i;
230
231 memset(winMsg, 0, sizeof(MSG));
232 win32wnd = Win32BaseWindow::GetWindowFromOS2Handle(os2Msg->hwnd);
233 if(!win32wnd) {
234 win32wnd = Win32BaseWindow::GetWindowFromOS2FrameHandle(os2Msg->hwnd);
235 if(win32wnd) {
236 fIsFrame = TRUE;
237 }
238 }
239
240 //PostThreadMessage posts WIN32APP_POSTMSG msg without window handle
241 //Realplayer starts a timer with hwnd 0 & proc 0; check this here
242 if(win32wnd == 0 && (os2Msg->msg != WM_CREATE && os2Msg->msg != WM_QUIT && os2Msg->msg != WM_TIMER && os2Msg->msg < WIN32APP_POSTMSG))
243 {
244 goto dummymessage; //not a win32 client window
245 }
246 winMsg->time = os2Msg->time;
247 //CB: PM bug or undocumented feature? ptl.x highword is set!
248 winMsg->pt.x = os2Msg->ptl.x & 0xFFFF;
249 winMsg->pt.y = mapScreenY(os2Msg->ptl.y);
250
251 if(win32wnd) //==0 for WM_CREATE/WM_QUIT
252 winMsg->hwnd = win32wnd->getWindowHandle();
253
254 if(os2Msg->msg >= WIN32APP_POSTMSG) {
255 packet = (POSTMSG_PACKET *)os2Msg->mp2;
256 if(packet && ((ULONG)os2Msg->mp1 == WIN32MSG_MAGICA || (ULONG)os2Msg->mp1 == WIN32MSG_MAGICW)) {
257 winMsg->message = os2Msg->msg - WIN32APP_POSTMSG;
258 winMsg->wParam = packet->wParam;
259 winMsg->lParam = packet->lParam;
260 if(fMsgRemoved == MSG_REMOVE) free(packet); //free the shared memory here
261 if(win32wnd) RELEASE_WNDOBJ(win32wnd);
262 return TRUE;
263 }
264 else {//broadcasted message (no packet present)
265 winMsg->message = os2Msg->msg - WIN32APP_POSTMSG;
266 winMsg->wParam = (UINT)os2Msg->mp1;
267 winMsg->lParam = (DWORD)os2Msg->mp2;
268 if(win32wnd) RELEASE_WNDOBJ(win32wnd);
269 return TRUE;
270 }
271 goto dummymessage;
272 }
273
274 switch(os2Msg->msg)
275 {
276 //OS/2 msgs
277 case WM_CREATE:
278 {
279 if(teb->o.odin.newWindow == 0) {
280 DebugInt3();
281 goto dummymessage;
282 }
283
284 win32wnd = (Win32BaseWindow *)teb->o.odin.newWindow;
285 win32wnd->addRef();
286
287 winMsg->message = WINWM_CREATE;
288 winMsg->hwnd = win32wnd->getWindowHandle();
289 winMsg->wParam = 0;
290 winMsg->lParam = (LPARAM)win32wnd->tmpcs;
291 break;
292 }
293
294 case WM_QUIT:
295 winMsg->message = WINWM_QUIT;
296 break;
297
298 case WM_CLOSE:
299 winMsg->message = WINWM_CLOSE;
300 break;
301
302 case WM_DESTROY:
303 winMsg->message = WINWM_DESTROY;
304 break;
305
306 case WM_ENABLE:
307 winMsg->message = WINWM_ENABLE;
308 winMsg->wParam = SHORT1FROMMP(os2Msg->mp1);
309 break;
310
311 case WM_SHOW:
312 winMsg->message = WINWM_SHOWWINDOW;
313 winMsg->wParam = SHORT1FROMMP(os2Msg->mp1);
314 break;
315
316 case WM_WINDOWPOSCHANGED:
317 {
318 PSWP pswp = (PSWP)os2Msg->mp1;
319 SWP swpOld = *(pswp + 1);
320 HWND hParent = NULLHANDLE;
321 LONG yDelta = pswp->cy - swpOld.cy;
322 LONG xDelta = pswp->cx - swpOld.cx;
323
324 if(!fIsFrame) goto dummymessage;
325
326 if ((pswp->fl & (SWP_SIZE | SWP_MOVE | SWP_ZORDER)) == 0) goto dummymessage;
327
328 if(pswp->fl & (SWP_MOVE | SWP_SIZE)) {
329 if (win32wnd->isChild()) {
330 if(win32wnd->getParent()) {
331 hParent = win32wnd->getParent()->getOS2WindowHandle();
332 }
333 else goto dummymessage; //parent has just been destroyed
334 }
335 }
336 if(win32wnd->getParent()) {
337 OSLibMapSWPtoWINDOWPOS(pswp, &teb->o.odin.wp, &swpOld, win32wnd->getParent()->getClientHeight(),
338 win32wnd->getOS2WindowHandle());
339 }
340 else OSLibMapSWPtoWINDOWPOS(pswp, &teb->o.odin.wp, &swpOld, OSLibQueryScreenHeight(), win32wnd->getOS2WindowHandle());
341
342 if (!win32wnd->CanReceiveSizeMsgs()) goto dummymessage;
343
344 if(pswp->fl & (SWP_MOVE | SWP_SIZE))
345 {
346 teb->o.odin.wp.hwnd = win32wnd->getWindowHandle();
347 if ((pswp->fl & SWP_ZORDER) && (pswp->hwndInsertBehind > HWND_BOTTOM))
348 {
349 Win32BaseWindow *wndAfter = Win32BaseWindow::GetWindowFromOS2Handle(pswp->hwndInsertBehind);
350 if(wndAfter) {
351 teb->o.odin.wp.hwndInsertAfter = wndAfter->getWindowHandle();
352 RELEASE_WNDOBJ(wndAfter);
353 }
354 else teb->o.odin.wp.hwndInsertAfter = HWND_TOP_W;
355 }
356 }
357 winMsg->message = WINWM_WINDOWPOSCHANGED;
358 winMsg->lParam = (LPARAM)&teb->o.odin.wp;
359 break;
360 }
361
362 case WM_ACTIVATE:
363 {
364 HWND hwndActivate = (HWND)os2Msg->mp2;
365 BOOL fMinimized = FALSE;
366
367 hwndActivate = OS2ToWin32Handle(hwndActivate);
368 if(hwndActivate == 0) {
369 //another (non-win32) application's window
370 //set to desktop window handle
371 hwndActivate = windowDesktop->getWindowHandle();
372 }
373
374 if(win32wnd->getStyle() & WS_MINIMIZE_W)
375 {
376 fMinimized = TRUE;
377 }
378
379 winMsg->message = WINWM_ACTIVATE;
380 winMsg->wParam = MAKELONG((SHORT1FROMMP(os2Msg->mp1)) ? WA_ACTIVE_W : WA_INACTIVE_W, fMinimized);
381 winMsg->lParam = (LPARAM)hwndActivate;
382 break;
383 }
384
385 case WM_SETFOCUS:
386 {
387 HWND hwndFocus = (HWND)os2Msg->mp1;
388
389 if(WinQueryWindowULong(hwndFocus, OFFSET_WIN32PM_MAGIC) != WIN32PM_MAGIC) {
390 //another (non-win32) application's window
391 //set to NULL (allowed according to win32 SDK) to avoid problems
392 hwndFocus = NULL;
393 }
394 else hwndFocus = OS2ToWin32Handle(hwndFocus);
395
396 if((ULONG)os2Msg->mp2 == TRUE) {
397 winMsg->message = WINWM_SETFOCUS;
398 winMsg->wParam = (WPARAM)hwndFocus;
399 }
400 else {
401 winMsg->message = WINWM_KILLFOCUS;
402 winMsg->wParam = (WPARAM)hwndFocus;
403 }
404 break;
405 }
406
407 //**************************************************************************
408 //Mouse messages (OS/2 Window coordinates -> Win32 coordinates relative to screen
409 //**************************************************************************
410 case WM_BUTTON1DOWN:
411 case WM_BUTTON1UP:
412 case WM_BUTTON1DBLCLK:
413 case WM_BUTTON2DOWN:
414 case WM_BUTTON2UP:
415 case WM_BUTTON2DBLCLK:
416 case WM_BUTTON3DOWN:
417 case WM_BUTTON3UP:
418 case WM_BUTTON3DBLCLK:
419 {
420 //WM_NC*BUTTON* is posted when the cursor is in a non-client area of the window
421
422 dprintf(("MsgButton %x (%x) %d at (%d,%d) time %x", winMsg->hwnd, os2Msg->hwnd, WINWM_NCLBUTTONDOWN + (os2Msg->msg - WM_BUTTON1DOWN), winMsg->pt.x, winMsg->pt.y, winMsg->time));
423
424 HWND hwnd;
425
426 DisableLogging();
427 if(GetCapture() != winMsg->hwnd)
428 {
429 hwnd = WindowFromPoint(winMsg->pt);
430 if(win32wnd->getWindowHandle() != hwnd) {
431 RELEASE_WNDOBJ(win32wnd);
432 win32wnd = Win32BaseWindow::GetWindowFromHandle(hwnd);
433 if(win32wnd == NULL) {
434 DebugInt3();
435 EnableLogging();
436 goto dummymessage;
437 }
438 winMsg->hwnd = hwnd;
439 }
440 }
441
442 //if a window is disabled, it's parent receives the mouse messages
443 if(!IsWindowEnabled(win32wnd->getWindowHandle())) {
444 if(win32wnd->getParent()) {
445 Win32BaseWindow *parent = win32wnd->getParent();;
446 if(parent) parent->addRef();
447 RELEASE_WNDOBJ(win32wnd);
448 win32wnd = parent;
449 }
450 fWasDisabled = TRUE;
451 }
452
453 if(IsNCMouseMsg(win32wnd)) {
454 winMsg->message = WINWM_NCLBUTTONDOWN + (os2Msg->msg - WM_BUTTON1DOWN);
455 winMsg->wParam = win32wnd->getLastHitTestVal();
456 winMsg->lParam = MAKELONG(winMsg->pt.x, winMsg->pt.y); //screen coordinates
457 }
458 else {
459 ClientPoint.x = winMsg->pt.x;
460 ClientPoint.y = winMsg->pt.y;
461 MapWindowPoints(0, win32wnd->getWindowHandle(), (LPPOINT)&ClientPoint, 1);
462 winMsg->message = WINWM_LBUTTONDOWN + (os2Msg->msg - WM_BUTTON1DOWN);
463 winMsg->wParam = GetMouseKeyState();
464 winMsg->lParam = MAKELONG(ClientPoint.x, ClientPoint.y); //client coordinates
465 }
466 EnableLogging();
467 if((fMsgRemoved == MSG_REMOVE) && ISMOUSE_CAPTURED())
468 {
469 if(DInputMouseHandler(win32wnd->getWindowHandle(), winMsg->message, winMsg->pt.x, winMsg->pt.y)) {
470 goto dummymessage; //dinput swallowed message
471 }
472 }
473
474 if(fWasDisabled) {
475 if(win32wnd) {
476 winMsg->hwnd = win32wnd->getWindowHandle();
477 }
478 else goto dummymessage; //don't send mouse messages to disabled windows
479 }
480
481 DisableLogging();
482 if ((winMsg->message == WINWM_LBUTTONDOWN) ||
483 (winMsg->message == WINWM_RBUTTONDOWN) ||
484 (winMsg->message == WINWM_MBUTTONDOWN) ||
485 (winMsg->message == WINWM_NCLBUTTONDOWN) ||
486 (winMsg->message == WINWM_NCRBUTTONDOWN) ||
487 (winMsg->message == WINWM_NCMBUTTONDOWN))
488 {
489 if(fGenerateDoubleClick && doubleClickMsg.message == winMsg->message &&
490 winMsg->time - doubleClickMsg.time < GetDoubleClickTime() &&
491 (abs(winMsg->pt.x - doubleClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK_W)/2) &&
492 (abs(winMsg->pt.y - doubleClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK_W)/2))
493 {
494 dprintf(("single -> double click"));
495 if(winMsg->message >= WINWM_LBUTTONDOWN) {
496 winMsg->message += (WINWM_LBUTTONDBLCLK - WINWM_LBUTTONDOWN);
497 }
498 else winMsg->message += (WINWM_LBUTTONDBLCLK - WINWM_NCLBUTTONDOWN);
499 doubleClickMsg.message = 0;
500 }
501 else {
502 dprintf(("save for double click"));
503 doubleClickMsg = *winMsg;
504 if(doubleClickMsg.message >= WINWM_NCLBUTTONDOWN && doubleClickMsg.message <= WINWM_NCMBUTTONDOWN) {
505 doubleClickMsg.message += (WINWM_LBUTTONDOWN - WINWM_NCLBUTTONDOWN);
506 }
507 }
508 }
509 EnableLogging();
510
511 break;
512 }
513
514 case WM_BUTTON2CLICK:
515 case WM_BUTTON1CLICK:
516 case WM_BUTTON3CLICK:
517 goto dummymessage;
518
519 case WM_BUTTON2MOTIONSTART:
520 case WM_BUTTON2MOTIONEND:
521 case WM_BUTTON1MOTIONSTART:
522 case WM_BUTTON1MOTIONEND:
523 case WM_BUTTON3MOTIONSTART:
524 case WM_BUTTON3MOTIONEND:
525 //no break; translate to WM_MOUSEMOVE
526 //Some applications (e.g. Unreal) retrieve all mouse messages
527 //when a mouse button is pressed and don't expect WM_NULL
528
529 case WM_MOUSEMOVE:
530 {
531 //WM_NCMOUSEMOVE is posted when the cursor moves into a non-client area of the window
532
533 HWND hwnd;
534
535 dprintf2(("WM_NCMOUSEMOVE (%d,%d)", winMsg->pt.x, winMsg->pt.y));
536 DisableLogging();
537 if(GetCapture() != winMsg->hwnd)
538 {
539 hwnd = WindowFromPoint(winMsg->pt);
540 if(win32wnd->getWindowHandle() != hwnd) {
541 RELEASE_WNDOBJ(win32wnd);
542 win32wnd = Win32BaseWindow::GetWindowFromHandle(hwnd);
543 if(win32wnd == NULL) {
544 DebugInt3();
545 EnableLogging();
546 goto dummymessage;
547 }
548 winMsg->hwnd = hwnd;
549 }
550 }
551
552 //if a window is disabled, it's parent receives the mouse messages
553 if(!IsWindowEnabled(win32wnd->getWindowHandle())) {
554 if(win32wnd->getParent()) {
555 Win32BaseWindow *parent = win32wnd->getParent();;
556 if(parent) parent->addRef();
557 RELEASE_WNDOBJ(win32wnd);
558 win32wnd = parent;
559 }
560 fWasDisabled = TRUE;
561 }
562 if(IsNCMouseMsg(win32wnd))
563 {
564 winMsg->message = WINWM_NCMOUSEMOVE;
565 winMsg->wParam = (WPARAM)win32wnd->getLastHitTestVal();
566 winMsg->lParam = MAKELONG(winMsg->pt.x,winMsg->pt.y);
567 }
568 else
569 {
570 ClientPoint.x = winMsg->pt.x;
571 ClientPoint.y = winMsg->pt.y;
572 MapWindowPoints(0, win32wnd->getWindowHandle(), (LPPOINT)&ClientPoint, 1);
573
574 winMsg->message = WINWM_MOUSEMOVE;
575 winMsg->wParam = GetMouseKeyState();
576 winMsg->lParam = MAKELONG(ClientPoint.x, ClientPoint.y); //client coordinates
577 }
578 EnableLogging();
579 if((fMsgRemoved == MSG_REMOVE) && ISMOUSE_CAPTURED())
580 {
581 if(DInputMouseHandler(win32wnd->getWindowHandle(), winMsg->message, winMsg->pt.x, winMsg->pt.y)) {
582 goto dummymessage; //dinput swallowed message
583 }
584 }
585 if(fWasDisabled) {
586 if(win32wnd) {
587 winMsg->hwnd = win32wnd->getWindowHandle();
588 }
589 else {
590 goto dummymessage; //don't send mouse messages to disabled windows
591 }
592 }
593 //OS/2 Window coordinates -> Win32 Window coordinates
594 break;
595 }
596
597 case WM_CONTROL:
598 goto dummymessage;
599
600 case WM_COMMAND:
601 if(SHORT1FROMMP(os2Msg->mp2) == CMDSRC_MENU) {
602 winMsg->message = WINWM_COMMAND;
603 winMsg->wParam = (WPARAM)SHORT1FROMMP(os2Msg->mp1); //id
604 break;
605 }
606 //todo controls
607 goto dummymessage;
608
609 case WM_SYSCOMMAND:
610 {
611 ULONG x = 0, y = 0;
612 ULONG win32sc;
613
614 if(SHORT2FROMMP(os2Msg->mp2) == TRUE) {//syscommand caused by mouse action
615 POINTL pointl;
616 WinQueryPointerPos(HWND_DESKTOP, &pointl);
617 x = pointl.x;
618 y = mapScreenY(y);
619 }
620 switch(SHORT1FROMMP(os2Msg->mp1)) {
621 case SC_MOVE:
622 win32sc = SC_MOVE_W;
623 break;
624 case SC_CLOSE:
625 {
626 //FALSE -> keyboard operation = user pressed Alt-F4 -> close app
627 //TRUE -> user clicked on close button -> close window
628 if(SHORT2FROMMP(os2Msg->mp2) == FALSE)
629 {
630 HWND hwnd = win32wnd->GetTopParent();
631 if(win32wnd->getWindowHandle() != hwnd) {
632 RELEASE_WNDOBJ(win32wnd);
633 win32wnd = Win32BaseWindow::GetWindowFromHandle(hwnd);
634 if(win32wnd == NULL) {
635 DebugInt3();
636 goto dummymessage;
637 }
638 winMsg->hwnd = hwnd;
639 }
640 }
641 win32sc = SC_CLOSE_W;
642 break;
643 }
644 case SC_MAXIMIZE:
645 win32sc = SC_MAXIMIZE_W;
646 break;
647 case SC_MINIMIZE:
648 win32sc = SC_MINIMIZE_W;
649 break;
650 case SC_NEXTFRAME:
651 case SC_NEXTWINDOW:
652 win32sc = SC_NEXTWINDOW_W;
653 break;
654 case SC_RESTORE:
655 win32sc = SC_RESTORE_W;
656 break;
657 case SC_TASKMANAGER:
658 win32sc = SC_TASKLIST_W;
659 break;
660 default:
661 dprintf(("Unknown/unsupported SC command %d", SHORT1FROMMP(os2Msg->mp1)));
662 goto dummymessage;
663 }
664 winMsg->message= WINWM_SYSCOMMAND;
665 winMsg->wParam = (WPARAM)win32sc;
666 winMsg->lParam = MAKELONG((USHORT)x, (USHORT)y);
667 break;
668 }
669
670 case WM_CHAR_SPECIAL:
671 {
672 // @@@PH
673 // special char message from the keyboard hook
674 dprintf(("PM: WM_CHAR_SPECIAL\n"));
675
676 // NO BREAK! FALLTHRU CASE!
677 }
678
679 case WM_CHAR:
680 {
681 ULONG repeatCount=0, virtualKey=0, keyFlags=0, scanCode=0;
682 ULONG flags = SHORT1FROMMP(os2Msg->mp1);
683 BOOL keyWasPressed, isExtended = FALSE;
684 char c;
685
686 teb->o.odin.fTranslated = FALSE;
687 repeatCount = CHAR3FROMMP(os2Msg->mp1);
688 scanCode = CHAR4FROMMP(os2Msg->mp1);
689 keyWasPressed = ((SHORT1FROMMP (os2Msg->mp1) & KC_PREVDOWN) == KC_PREVDOWN);
690
691 dprintf(("PM: WM_CHAR: %x %x rep=%d scancode=%x", SHORT1FROMMP(os2Msg->mp2), SHORT2FROMMP(os2Msg->mp2), repeatCount, scanCode));
692 dprintf(("PM: WM_CHAR: hwnd %x flags %x mp1 %x, mp2 %x, time=%08xh", win32wnd->getWindowHandle(), flags, os2Msg->mp1, os2Msg->mp2, os2Msg->time));
693
694 // vitali add begin
695 if ( ( SHORT1FROMMP(os2Msg->mp2) & 0x0FF ) == 0x0E0 )
696 {
697 // an extended key ( arrows, ins, del and so on )
698 // get "virtual" scancode from character code because
699 // for "regular" keys they are equal
700 if(!(flags & (KC_SHIFT|KC_ALT|KC_CTRL))) {
701 scanCode = ( SHORT1FROMMP(os2Msg->mp2) >> 8) & 0x0FF;
702 }
703 isExtended = TRUE;
704 }
705 // vitali add end
706
707 // both WM_KEYUP & WM_KEYDOWN want a virtual key, find the right Win32 virtual key
708 // given the OS/2 virtual key and OS/2 character
709
710 //if (((SHORT1FROMMP (mp1) & KC_CHAR) == KC_CHAR) ||
711 // ((SHORT1FROMMP (mp1) & KC_LONEKEY) == KC_LONEKEY))
712 c = 0;
713 if ((SHORT1FROMMP (os2Msg->mp1) & 0xFF) != 0)
714 {
715 c = SHORT1FROMMP (os2Msg->mp2);
716 if ((c >= 'A') && (c <= 'Z')) {
717 virtualKey = c;
718 goto VirtualKeyFound;
719 }
720 if ((c >='a') && (c <= 'z')) {
721 virtualKey = c - 32; // make it uppercase
722 goto VirtualKeyFound;
723 }
724 if ((c >= '0') && (c <= '9')) {
725 virtualKey = c;
726 goto VirtualKeyFound;
727 }
728 }
729
730VirtualKeyFound:
731// dprintf (("VIRTUALKEYFOUND:(%x)", virtualKey));
732
733 // Adjust PM scancodes for Win* keys
734 if (scanCode >= 0x70)
735 scanCode -= 0x10;
736 winMsg->wParam = pmscan2winkey[scanCode][0];
737 winMsg->lParam = repeatCount & 0x0FFFF; // bit 0-15, repeatcount
738 winMsg->lParam |= (pmscan2winkey[scanCode][1] & 0x1FF) << 16; // bit 16-23, scancode + bit 15 extended
739
740 // Adjust VKEY value for pad digits if NumLock is on
741 if ((scanCode >= 0x47) && (scanCode <= 0x53) &&
742 (virtualKey >= 0x30) && (virtualKey >= 39))
743 winMsg->wParam = virtualKey + 0x30;
744
745 // Set the extended bit when appropriate
746 if (isExtended)
747 winMsg->lParam = winMsg->lParam | (1<<24);
748
749 if (!(flags & KC_ALT))
750 {
751 //
752 // the Alt key is not pressed
753 // or no more pressed
754 //
755 if (flags & KC_KEYUP)
756 {
757 // check for a lonesome ALT key ...
758 if ( (flags & KC_LONEKEY) &&
759 (winMsg->wParam == VK_LMENU_W) )
760 {
761 winMsg->message = WINWM_SYSKEYUP;
762
763 // held ALT-key when current key is released
764 // generates additional flag 0x2000000
765 // Note: PM seems to do this differently,
766 // KC_ALT is already reset
767 }
768 else
769 {
770 // send WM_KEYUP message
771 winMsg->message = WINWM_KEYUP;
772 }
773
774 winMsg->lParam |= 1 << 30; // bit 30, previous state, always 1 for a WM_KEYUP message
775 winMsg->lParam |= 1 << 31; // bit 31, transition state, always 1 for WM_KEYUP
776 }
777 else
778 {
779 // send WM_KEYDOWN message
780 winMsg->message = WINWM_KEYDOWN;
781 if (keyWasPressed)
782 winMsg->lParam |= 1 << 30; // bit 30, previous state, 1 means key was pressed
783 }
784 }
785 else
786 {
787 //
788 // the Alt key is pressed
789 //
790 if (flags & KC_KEYUP)
791 {
792 // send WM_SYSKEYUP message
793 winMsg->message = WINWM_SYSKEYUP;
794 winMsg->lParam |= 1 << 30; // bit 30, previous state, always 1 for a WM_KEYUP message
795 winMsg->lParam |= 1 << 31; // bit 31, transition state, always 1 for WM_KEYUP
796 }
797 else
798 {
799 // send WM_SYSKEYDOWN message
800 winMsg->message = WINWM_SYSKEYDOWN;
801 if (keyWasPressed)
802 winMsg->lParam |= 1 << 30; // bit 30, previous state, 1 means key was pressed
803
804 // pressed ALT-key generates additional flag 0x2000000
805 // if the current window has keyboard focus
806 winMsg->lParam |= 1 << 29;
807 }
808 }
809
810 // ------------------------------------------------
811 // check if additional messages have to be recorded
812 // ------------------------------------------------
813#if 0
814 {
815 MSG extramsg;
816 extramsg.hwnd = winMsg->hwnd;
817 extramsg.time = winMsg->time;
818 extramsg.pt = winMsg->pt;
819
820 switch (scanCode)
821 {
822 case PMSCAN_PRINT:
823 // Note: PRINT generates a WM_KEYUP under Windows
824 // also only call the standard kbd hook for the WM_KEYUP msg,
825 // the low-level hook is called twice
826 // mb->putWinMsg(hwndWin32, WM_CHAR, VK_PRINT_W, ... )
827 // WM_CHAR(0x0000002ch, c1370001h)
828 extramsg.message = WINWM_KEYUP;
829 extramsg.wParam = VK_PRINT_W;
830 extramsg.lParam = winMsg->lParam;
831 setThreadQueueExtraCharMessage(teb, &extramsg);
832 break;
833
834 #define WIN_KEY_EXTENDED 0x01000000
835 #define WIN_KEY_DONTCARE 0x02000000
836 #define WIN_KEY_ALTHELD 0x20000000
837 #define WIN_KEY_PREVSTATE 0x40000000
838 case PMSCAN_ALTRIGHT:
839 {
840 // we need very special treatment here for the
841 // poor, crippled AltGr key
842 // Note: see fKeyAltGrDown above!
843
844 if ( (keyWasPressed) || (fKeyAltGrDown == FALSE) )
845 {
846 fKeyAltGrDown = TRUE;
847
848 // 1 - generate a virtual LCONTROL-keypress
849 // 2 - send LMENU-keypress (NT emulates ALtGr w/ Ctrl-AltGr!)
850 // 0xfe000000: mask out extended-key, scancode, repeatcount
851 // Note: Win sends a WINWM_SYSKEYDOWN to the hooks,
852 // the queue gets a WM_KEYDOWN only! (wrong impl. here)
853 extramsg.message = WINWM_KEYDOWN;
854 extramsg.wParam = VK_RMENU_W;
855 extramsg.lParam = (winMsg->lParam & 0xfe000000)
856 | WIN_KEY_EXTENDED
857 | (keyWasPressed ? WIN_KEY_PREVSTATE : 0)
858 | WIN_KEY_ALTHELD
859 | repeatCount
860 | (WINSCAN_ALTRIGHT << 16);
861 winMsg->message = WINWM_KEYDOWN;
862 winMsg->wParam = VK_LCONTROL_W;
863 winMsg->lParam = (winMsg->lParam & 0xfe000000)
864 | WIN_KEY_DONTCARE
865 | (keyWasPressed ? WIN_KEY_PREVSTATE : 0)
866 | WIN_KEY_ALTHELD
867 | repeatCount
868 | (WINSCAN_CTRLLEFT << 16);
869 }
870 else
871 {
872 // OK, now we can release it!
873 fKeyAltGrDown = FALSE;
874
875 // key up
876 // 1 - generate a virtual LCONTROL-keypress
877 // 2 - send LMENU-keypress (NT emulates ALtGr w/ Ctrl-Alt!)
878 extramsg.message = WINWM_KEYUP;
879 extramsg.wParam = VK_RMENU_W;
880 extramsg.lParam = (winMsg->lParam & 0xfe000000)
881 | WIN_KEY_EXTENDED
882 | repeatCount
883 | (WINSCAN_ALTRIGHT << 16);
884 winMsg->message = WINWM_SYSKEYUP;
885 winMsg->wParam = VK_LCONTROL_W;
886 winMsg->lParam = (winMsg->lParam & 0xfe000000)
887 | WIN_KEY_ALTHELD
888 | repeatCount
889 | (WINSCAN_CTRLLEFT << 16);
890 }
891
892 // @@@PH
893 // unknown: this leads to an infinite loop
894 // because the OS/2 WM_CHAR_SPECIAL message is never
895 // removed from the queue?!
896 setThreadQueueExtraCharMessage(teb, &extramsg);
897
898 if ( (keyWasPressed) || (fKeyAltGrDown == FALSE) )
899 {
900 fKeyAltGrDown = TRUE;
901
902 // 1 - generate a virtual LCONTROL-keypress
903 // 2 - send LMENU-keypress (NT emulates ALtGr w/ Ctrl-AltGr!)
904 // 0xfe000000: mask out extended-key, scancode, repeatcount
905 // Note: Win sends a WINWM_SYSKEYDOWN to the hooks,
906 // the queue gets a WM_KEYDOWN only! (wrong impl. here)
907 winMsg->message = WINWM_KEYDOWN;
908 winMsg->wParam = VK_RMENU_W;
909 winMsg->lParam = (winMsg->lParam & 0xfe000000)
910 | WIN_KEY_EXTENDED
911 | (keyWasPressed ? WIN_KEY_PREVSTATE : 0)
912 | WIN_KEY_ALTHELD
913 | repeatCount
914 | (WINSCAN_ALTRIGHT << 16);
915 }
916 else
917 {
918 // OK, now we can release it!
919 fKeyAltGrDown = FALSE;
920
921 // key up
922 // 1 - generate a virtual LCONTROL-keypress
923 // 2 - send LMENU-keypress (NT emulates ALtGr w/ Ctrl-Alt!)
924 winMsg->message = WINWM_KEYUP;
925 winMsg->wParam = VK_RMENU_W;
926 winMsg->lParam = (winMsg->lParam & 0xfe000000)
927 | WIN_KEY_EXTENDED
928 | repeatCount
929 | (WINSCAN_ALTRIGHT << 16);
930 }
931 }
932 break;
933 } /* switch */
934 }
935#endif
936
937 if (ISKDB_CAPTURED())
938 {
939 if (DInputKeyBoardHandler(winMsg)) {
940 goto dummymessage; //dinput swallowed message
941 }
942 }
943 break;
944 }
945
946 case WM_TIMER:
947//Why was this check here????
948// if (os2Msg->mp2)
949// {
950 BOOL sys;
951 ULONG id;
952
953 if (TIMER_GetTimerInfo(os2Msg->hwnd,(ULONG)os2Msg->mp1,&sys,&id))
954 {
955 winMsg->wParam = (WPARAM)id;
956 winMsg->message= (sys) ? WINWM_SYSTIMER : WINWM_TIMER;
957 break;
958 }
959// }
960 goto dummymessage; //for caret blinking
961
962 case WM_SETWINDOWPARAMS:
963 {
964 WNDPARAMS *wndParams = (WNDPARAMS *)os2Msg->mp1;
965
966 if(wndParams->fsStatus & WPM_TEXT) {
967 winMsg->message = WINWM_SETTEXT;
968 winMsg->lParam = (LPARAM)wndParams->pszText;
969 break;
970 }
971 goto dummymessage;
972 }
973
974#if 0
975 case WM_QUERYWINDOWPARAMS:
976 {
977 PWNDPARAMS wndpars = (PWNDPARAMS)mp1;
978 ULONG textlen;
979 PSZ wintext;
980
981 if(wndpars->fsStatus & (WPM_CCHTEXT | WPM_TEXT))
982 {
983 if(wndpars->fsStatus & WPM_CCHTEXT)
984 wndpars->cchText = win32wnd->MsgGetTextLength();
985 if(wndpars->fsStatus & WPM_TEXT)
986 wndpars->pszText = win32wnd->MsgGetText();
987
988 wndpars->fsStatus = 0;
989 wndpars->cbCtlData = 0;
990 wndpars->cbPresParams = 0;
991 goto dummymessage;
992 }
993 }
994#endif
995
996 case WM_PAINT:
997 {
998 if(win32wnd->IsWindowIconic()) {
999 winMsg->message = WINWM_PAINTICON;
1000 }
1001 else winMsg->message = WINWM_PAINT;
1002 break;
1003 }
1004
1005 case WM_CONTEXTMENU:
1006 winMsg->message = WINWM_CONTEXTMENU;
1007 winMsg->wParam = win32wnd->getWindowHandle();
1008 winMsg->lParam = MAKELONG(winMsg->pt.x,winMsg->pt.y);
1009 break;
1010
1011 case WM_RENDERFMT:
1012 winMsg->message = WINWM_RENDERFORMAT;
1013 winMsg->wParam = (UINT) os2Msg->mp1;
1014 break;
1015
1016 case WM_RENDERALLFMTS:
1017 winMsg->message = WINWM_RENDERALLFORMATS;
1018 break;
1019
1020 case WM_DESTROYCLIPBOARD:
1021 winMsg->message = WINWM_DESTROYCLIPBOARD;
1022 break;
1023
1024 case WM_HSCROLL:
1025 case WM_VSCROLL:
1026 winMsg->message = (os2Msg->msg == WM_HSCROLL) ? WINWM_HSCROLL : WINWM_VSCROLL;
1027 winMsg->lParam = 0;
1028 winMsg->wParam = 0;
1029 switch(SHORT2FROMMP(os2Msg->mp2)) {
1030 case SB_LINERIGHT:
1031 winMsg->wParam = SB_LINERIGHT_W;
1032 break;
1033 case SB_LINELEFT:
1034 winMsg->wParam = SB_LINELEFT_W;
1035 break;
1036 case SB_PAGELEFT:
1037 winMsg->wParam = SB_PAGELEFT_W;
1038 break;
1039 case SB_PAGERIGHT:
1040 winMsg->wParam = SB_PAGERIGHT_W;
1041 break;
1042 default:
1043 dprintf(("Unsupported WM_H/VSCROLL message %x!!", SHORT2FROMMP(os2Msg->mp2)));
1044 goto dummymessage;
1045 }
1046 break;
1047
1048 case WM_INITMENU:
1049 case WM_MENUSELECT:
1050 case WM_MENUEND:
1051 case WM_NEXTMENU:
1052 case WM_SYSCOLORCHANGE:
1053 case WM_SYSVALUECHANGED:
1054 case WM_SETSELECTION:
1055 case WM_PPAINT:
1056 case WM_PSETFOCUS:
1057 case WM_PSYSCOLORCHANGE:
1058 case WM_PSIZE:
1059 case WM_PACTIVATE:
1060 case WM_PCONTROL:
1061 case WM_HELP:
1062 case WM_APPTERMINATENOTIFY:
1063 case WM_PRESPARAMCHANGED:
1064 case WM_DRAWITEM:
1065 case WM_MEASUREITEM:
1066 case WM_CONTROLPOINTER:
1067 case WM_QUERYDLGCODE:
1068 case WM_SUBSTITUTESTRING:
1069 case WM_MATCHMNEMONIC:
1070 case WM_SAVEAPPLICATION:
1071 case WM_SEMANTICEVENT:
1072 default:
1073dummymessage:
1074 dprintf2(("dummy message %x %x %x %x", os2Msg->hwnd, os2Msg->msg, os2Msg->mp1, os2Msg->mp2));
1075 winMsg->message = 0;
1076 winMsg->wParam = 0;
1077 winMsg->lParam = 0;
1078 if(win32wnd) RELEASE_WNDOBJ(win32wnd);
1079 return FALSE;
1080 }
1081 if(win32wnd) RELEASE_WNDOBJ(win32wnd);
1082 return TRUE;
1083}
1084//******************************************************************************
1085//******************************************************************************
1086BOOL OSLibWinTranslateMessage(MSG *msg)
1087{
1088 TEB *teb;
1089
1090 teb = GetThreadTEB();
1091 if(!teb) {
1092 return FALSE;
1093 }
1094 //NOTE: These actually need to be posted so that the next message retrieved by GetMessage contains
1095 // the newly generated WM_CHAR message.
1096 if(!teb->o.odin.fTranslated && teb->o.odin.os2msg.msg == WM_CHAR && !((SHORT1FROMMP(teb->o.odin.os2msg.mp1) & KC_KEYUP) == KC_KEYUP))
1097 {
1098 //TranslatedMessage was called before DispatchMessage, so queue WM_CHAR message
1099 ULONG fl = SHORT1FROMMP(teb->o.odin.os2msg.mp1);
1100 MSG extramsg;
1101
1102 memcpy(&extramsg, msg, sizeof(MSG));
1103 extramsg.wParam = SHORT1FROMMP(teb->o.odin.os2msg.mp2);
1104 extramsg.lParam = 0;
1105
1106 // ESCAPE generates a WM_CHAR under windows, so take
1107 // special care for this here.
1108 UCHAR ucPMScanCode = CHAR4FROMMP(teb->o.odin.os2msg.mp1);
1109 switch (ucPMScanCode)
1110 {
1111 case PMSCAN_ESC:
1112 extramsg.wParam = VK_ESCAPE_W;
1113 fl |= KC_CHAR;
1114 break;
1115 }
1116
1117 if(!(fl & KC_CHAR) && msg->message < WINWM_SYSKEYDOWN)
1118 {
1119 return FALSE;
1120 }
1121
1122 if(fl & KC_VIRTUALKEY)
1123 {
1124 if(msg->wParam)
1125 {
1126 if ((msg->wParam >= VK_NUMPAD0_W) &&
1127 (msg->wParam <= VK_NUMPAD9_W))
1128 extramsg.wParam = msg->wParam - 0x30;
1129 else
1130 extramsg.wParam = msg->wParam;
1131 }
1132 else
1133 extramsg.wParam = SHORT2FROMMP(teb->o.odin.os2msg.mp2);
1134 }
1135
1136
1137 if(msg->message >= WINWM_SYSKEYDOWN)
1138 extramsg.message = WINWM_SYSCHAR;
1139 else
1140 extramsg.message = WINWM_CHAR;
1141
1142 if(fl & KC_DEADKEY)
1143 extramsg.message++; //WM_DEADCHAR/WM_SYSDEADCHAR
1144
1145
1146 extramsg.lParam = msg->lParam & 0x00FFFFFF;
1147 if(fl & KC_ALT)
1148 extramsg.lParam |= (1<<29);
1149 if(fl & KC_PREVDOWN)
1150 extramsg.lParam |= (1<<30);
1151 if(fl & KC_KEYUP)
1152 extramsg.lParam |= (1<<31);
1153
1154 // insert message into the queue
1155 setThreadQueueExtraCharMessage(teb, &extramsg);
1156 return TRUE;
1157 }
1158 return FALSE;
1159}
1160//******************************************************************************
1161//******************************************************************************
1162
Note: See TracBrowser for help on using the repository browser.