ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/AmigaOS/video_amiga.cpp
Revision: 1.7
Committed: 2000-07-22T16:20:55Z (24 years, 4 months ago) by cebix
Branch: MAIN
Changes since 1.6: +2 -2 lines
Log Message:
- fixed compilation problems with CyberGraphX code

File Contents

# Content
1 /*
2 * video_amiga.cpp - Video/graphics emulation, AmigaOS specific stuff
3 *
4 * Basilisk II (C) 1997-2000 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include <exec/types.h>
22 #include <intuition/intuition.h>
23 #include <graphics/rastport.h>
24 #include <graphics/gfx.h>
25 #include <cybergraphics/cybergraphics.h>
26 #include <dos/dostags.h>
27 #include <devices/timer.h>
28 #include <proto/exec.h>
29 #include <proto/dos.h>
30 #include <proto/intuition.h>
31 #include <proto/graphics.h>
32 #include <proto/Picasso96.h>
33 #include <proto/cybergraphics.h>
34
35 #include "sysdeps.h"
36 #include "main.h"
37 #include "adb.h"
38 #include "prefs.h"
39 #include "user_strings.h"
40 #include "video.h"
41
42 #define DEBUG 0
43 #include "debug.h"
44
45
46 // Display types
47 enum {
48 DISPLAY_WINDOW,
49 DISPLAY_PIP,
50 DISPLAY_SCREEN_P96,
51 DISPLAY_SCREEN_CGFX
52 };
53
54 // Global variables
55 static int32 frame_skip;
56 static int display_type = DISPLAY_WINDOW; // See enum above
57 static struct Screen *the_screen = NULL;
58 static struct Window *the_win = NULL;
59 static struct BitMap *the_bitmap = NULL;
60 static LONG black_pen = -1, white_pen = -1;
61 static struct Process *periodic_proc = NULL; // Periodic process
62
63 extern struct Task *MainTask; // Pointer to main task (from main_amiga.cpp)
64
65
66 // Amiga -> Mac raw keycode translation table
67 static const uint8 keycode2mac[0x80] = {
68 0x0a, 0x12, 0x13, 0x14, 0x15, 0x17, 0x16, 0x1a, // ` 1 2 3 4 5 6 7
69 0x1c, 0x19, 0x1d, 0x1b, 0x18, 0x2a, 0xff, 0x52, // 8 9 0 - = \ inv 0
70 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x10, 0x20, 0x22, // Q W E R T Y U I
71 0x1f, 0x23, 0x21, 0x1e, 0xff, 0x53, 0x54, 0x55, // O P [ ] inv 1 2 3
72 0x00, 0x01, 0x02, 0x03, 0x05, 0x04, 0x26, 0x28, // A S D F G H J K
73 0x25, 0x29, 0x27, 0x2a, 0xff, 0x56, 0x57, 0x58, // L ; ' # inv 4 5 6
74 0x32, 0x06, 0x07, 0x08, 0x09, 0x0b, 0x2d, 0x2e, // < Z X C V B N M
75 0x2b, 0x2f, 0x2c, 0xff, 0x41, 0x59, 0x5b, 0x5c, // , . / inv . 7 8 9
76 0x31, 0x33, 0x30, 0x4c, 0x24, 0x35, 0x75, 0xff, // SPC BSP TAB ENT RET ESC DEL inv
77 0xff, 0xff, 0x4e, 0xff, 0x3e, 0x3d, 0x3c, 0x3b, // inv inv - inv CUP CDN CRT CLF
78 0x7a, 0x78, 0x63, 0x76, 0x60, 0x61, 0x62, 0x64, // F1 F2 F3 F4 F5 F6 F7 F8
79 0x65, 0x6d, 0x47, 0x51, 0x4b, 0x43, 0x45, 0x72, // F9 F10 ( ) / * + HLP
80 0x38, 0x38, 0x39, 0x36, 0x3a, 0x3a, 0x37, 0x37, // SHL SHR CAP CTL ALL ALR AML AMR
81 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // inv inv inv inv inv inv inv inv
82 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // inv inv inv inv inv inv inv inv
83 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // inv inv inv inv inv inv inv inv
84 };
85
86
87 // Prototypes
88 static void periodic_func(void);
89
90
91 /*
92 * Initialization
93 */
94
95 // Open window
96 static bool init_window(int width, int height)
97 {
98 // Set absolute mouse mode
99 ADBSetRelMouseMode(false);
100
101 // Open window
102 the_win = OpenWindowTags(NULL,
103 WA_Left, 0, WA_Top, 0,
104 WA_InnerWidth, width, WA_InnerHeight, height,
105 WA_SimpleRefresh, TRUE,
106 WA_NoCareRefresh, TRUE,
107 WA_Activate, TRUE,
108 WA_RMBTrap, TRUE,
109 WA_ReportMouse, TRUE,
110 WA_DragBar, TRUE,
111 WA_DepthGadget, TRUE,
112 WA_SizeGadget, FALSE,
113 WA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
114 TAG_END
115 );
116 if (the_win == NULL) {
117 ErrorAlert(GetString(STR_OPEN_WINDOW_ERR));
118 return false;
119 }
120
121 // Create bitmap ("height + 2" for safety)
122 the_bitmap = AllocBitMap(width, height + 2, 1, BMF_CLEAR, NULL);
123 if (the_bitmap == NULL) {
124 ErrorAlert(GetString(STR_NO_MEM_ERR));
125 return false;
126 }
127
128 // Set VideoMonitor
129 VideoMonitor.mac_frame_base = (uint32)the_bitmap->Planes[0];
130 VideoMonitor.bytes_per_row = the_bitmap->BytesPerRow;
131 VideoMonitor.x = width;
132 VideoMonitor.y = height;
133 VideoMonitor.mode = VMODE_1BIT;
134
135 // Set FgPen and BgPen
136 black_pen = ObtainBestPenA(the_win->WScreen->ViewPort.ColorMap, 0, 0, 0, NULL);
137 white_pen = ObtainBestPenA(the_win->WScreen->ViewPort.ColorMap, 0xffffffff, 0xffffffff, 0xffffffff, NULL);
138 SetAPen(the_win->RPort, black_pen);
139 SetBPen(the_win->RPort, white_pen);
140 SetDrMd(the_win->RPort, JAM2);
141 return true;
142 }
143
144 // Open PIP (requires Picasso96)
145 static bool init_pip(int width, int height)
146 {
147 // Set absolute mouse mode
148 ADBSetRelMouseMode(false);
149
150 // Open window
151 ULONG error = 0;
152 the_win = p96PIP_OpenTags(
153 P96PIP_SourceFormat, RGBFB_R5G5B5,
154 P96PIP_SourceWidth, width,
155 P96PIP_SourceHeight, height,
156 P96PIP_ErrorCode, (ULONG)&error,
157 WA_Left, 0, WA_Top, 0,
158 WA_InnerWidth, width, WA_InnerHeight, height,
159 WA_SimpleRefresh, TRUE,
160 WA_NoCareRefresh, TRUE,
161 WA_Activate, TRUE,
162 WA_RMBTrap, TRUE,
163 WA_ReportMouse, TRUE,
164 WA_DragBar, TRUE,
165 WA_DepthGadget, TRUE,
166 WA_SizeGadget, FALSE,
167 WA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
168 WA_PubScreenName, (ULONG)"Workbench",
169 TAG_END
170 );
171 if (the_win == NULL || error) {
172 ErrorAlert(GetString(STR_OPEN_WINDOW_ERR));
173 return false;
174 }
175
176 // Find bitmap
177 p96PIP_GetTags(the_win, P96PIP_SourceBitMap, (ULONG)&the_bitmap, TAG_END);
178
179 // Set VideoMonitor
180 VideoMonitor.mac_frame_base = p96GetBitMapAttr(the_bitmap, P96BMA_MEMORY);
181 VideoMonitor.bytes_per_row = p96GetBitMapAttr(the_bitmap, P96BMA_BYTESPERROW);
182 VideoMonitor.x = width;
183 VideoMonitor.y = height;
184 VideoMonitor.mode = VMODE_16BIT;
185 return true;
186 }
187
188 // Open Picasso96 screen
189 static bool init_screen_p96(ULONG mode_id)
190 {
191 // Set relative mouse mode
192 ADBSetRelMouseMode(true);
193
194 // Check if the mode is one we can handle
195 uint32 depth = p96GetModeIDAttr(mode_id, P96IDA_DEPTH);
196 uint32 format = p96GetModeIDAttr(mode_id, P96IDA_RGBFORMAT);
197
198 switch (depth) {
199 case 8:
200 VideoMonitor.mode = VMODE_8BIT;
201 break;
202 case 15:
203 case 16:
204 if (format != RGBFB_R5G5B5) {
205 ErrorAlert(GetString(STR_WRONG_SCREEN_FORMAT_ERR));
206 return false;
207 }
208 VideoMonitor.mode = VMODE_16BIT;
209 break;
210 case 24:
211 case 32:
212 if (format != RGBFB_A8R8G8B8) {
213 ErrorAlert(GetString(STR_WRONG_SCREEN_FORMAT_ERR));
214 return false;
215 }
216 VideoMonitor.mode = VMODE_32BIT;
217 break;
218 default:
219 ErrorAlert(GetString(STR_WRONG_SCREEN_DEPTH_ERR));
220 return false;
221 }
222
223 // Yes, get width and height
224 uint32 width = p96GetModeIDAttr(mode_id, P96IDA_WIDTH);
225 uint32 height = p96GetModeIDAttr(mode_id, P96IDA_HEIGHT);
226
227 VideoMonitor.x = width;
228 VideoMonitor.y = height;
229
230 // Open screen
231 the_screen = p96OpenScreenTags(
232 P96SA_DisplayID, mode_id,
233 P96SA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
234 P96SA_Quiet, TRUE,
235 P96SA_NoMemory, TRUE,
236 P96SA_NoSprite, TRUE,
237 P96SA_Exclusive, TRUE,
238 TAG_END
239 );
240 if (the_screen == NULL) {
241 ErrorAlert(GetString(STR_OPEN_SCREEN_ERR));
242 return false;
243 }
244
245 // Open window
246 the_win = OpenWindowTags(NULL,
247 WA_Left, 0, WA_Top, 0,
248 WA_Width, width, WA_Height, height,
249 WA_NoCareRefresh, TRUE,
250 WA_Borderless, TRUE,
251 WA_Activate, TRUE,
252 WA_RMBTrap, TRUE,
253 WA_ReportMouse, TRUE,
254 WA_CustomScreen, (ULONG)the_screen,
255 TAG_END
256 );
257 if (the_win == NULL) {
258 ErrorAlert(GetString(STR_OPEN_WINDOW_ERR));
259 return false;
260 }
261
262 // Set VideoMonitor
263 ScreenToFront(the_screen);
264 VideoMonitor.mac_frame_base = p96GetBitMapAttr(the_screen->RastPort.BitMap, P96BMA_MEMORY);
265 VideoMonitor.bytes_per_row = p96GetBitMapAttr(the_screen->RastPort.BitMap, P96BMA_BYTESPERROW);
266 return true;
267 }
268
269 // Open CyberGraphX screen
270 static bool init_screen_cgfx(ULONG mode_id)
271 {
272 // Set relative mouse mode
273 ADBSetRelMouseMode(true);
274
275 // Check if the mode is one we can handle
276 uint32 depth = GetCyberIDAttr(CYBRIDATTR_DEPTH, mode_id);
277 uint32 format = GetCyberIDAttr(CYBRIDATTR_PIXFMT, mode_id);
278
279 switch (depth) {
280 case 8:
281 VideoMonitor.mode = VMODE_8BIT;
282 break;
283 case 15:
284 case 16:
285 if (format != PIXFMT_RGB16) {
286 ErrorAlert(GetString(STR_WRONG_SCREEN_FORMAT_ERR));
287 return false;
288 }
289 VideoMonitor.mode = VMODE_16BIT;
290 break;
291 case 24:
292 case 32:
293 if (format != PIXFMT_ARGB32) {
294 ErrorAlert(GetString(STR_WRONG_SCREEN_FORMAT_ERR));
295 return false;
296 }
297 VideoMonitor.mode = VMODE_32BIT;
298 break;
299 default:
300 ErrorAlert(GetString(STR_WRONG_SCREEN_DEPTH_ERR));
301 return false;
302 }
303
304 // Yes, get width and height
305 uint32 width = GetCyberIDAttr(CYBRIDATTR_WIDTH, mode_id);
306 uint32 height = GetCyberIDAttr(CYBRIDATTR_HEIGHT, mode_id);
307
308 VideoMonitor.x = width;
309 VideoMonitor.y = height;
310
311 // Open screen
312 the_screen = OpenScreenTags(NULL,
313 SA_DisplayID, mode_id,
314 SA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
315 SA_Quiet, TRUE,
316 SA_Exclusive, TRUE,
317 TAG_END
318 );
319 if (the_screen == NULL) {
320 ErrorAlert(GetString(STR_OPEN_SCREEN_ERR));
321 return false;
322 }
323
324 // Open window
325 the_win = OpenWindowTags(NULL,
326 WA_Left, 0, WA_Top, 0,
327 WA_Width, width, WA_Height, height,
328 WA_NoCareRefresh, TRUE,
329 WA_Borderless, TRUE,
330 WA_Activate, TRUE,
331 WA_RMBTrap, TRUE,
332 WA_ReportMouse, TRUE,
333 WA_CustomScreen, (ULONG)the_screen,
334 TAG_END
335 );
336 if (the_win == NULL) {
337 ErrorAlert(GetString(STR_OPEN_WINDOW_ERR));
338 return false;
339 }
340
341 // Set VideoMonitor
342 ScreenToFront(the_screen);
343 static UWORD ptr[] = { 0, 0, 0, 0 };
344 SetPointer(the_win, ptr, 0, 0, 0, 0); // Hide mouse pointer
345 APTR handle = LockBitMapTags(the_screen->RastPort.BitMap,
346 LBMI_BASEADDRESS, (ULONG)&VideoMonitor.mac_frame_base,
347 TAG_END);
348 UnLockBitMap(handle);
349 VideoMonitor.bytes_per_row = GetCyberMapAttr(the_screen->RastPort.BitMap, CYBRMATTR_XMOD);
350 return true;
351 }
352
353 bool VideoInit(bool classic)
354 {
355 // Read frame skip prefs
356 frame_skip = PrefsFindInt32("frameskip");
357 if (frame_skip == 0)
358 frame_skip = 1;
359
360 // Get screen mode from preferences
361 const char *mode_str;
362 if (classic)
363 mode_str = "win/512/342";
364 else
365 mode_str = PrefsFindString("screen");
366
367 // Determine type and mode
368 display_type = DISPLAY_WINDOW;
369 int width = 512, height = 384;
370 ULONG mode_id = 0;
371 if (mode_str) {
372 if (sscanf(mode_str, "win/%d/%d", &width, &height) == 2)
373 display_type = DISPLAY_WINDOW;
374 else if (sscanf(mode_str, "pip/%d/%d", &width, &height) == 2 && P96Base)
375 display_type = DISPLAY_PIP;
376 else if (sscanf(mode_str, "scr/%08lx", &mode_id) == 1 && (CyberGfxBase || P96Base)) {
377 if (P96Base && p96GetModeIDAttr(mode_id, P96IDA_ISP96))
378 display_type = DISPLAY_SCREEN_P96;
379 else if (CyberGfxBase && IsCyberModeID(mode_id))
380 display_type = DISPLAY_SCREEN_CGFX;
381 else {
382 ErrorAlert(GetString(STR_NO_P96_MODE_ERR));
383 return false;
384 }
385 }
386 }
387
388 // Open display
389 switch (display_type) {
390 case DISPLAY_WINDOW:
391 if (!init_window(width, height))
392 return false;
393 break;
394
395 case DISPLAY_PIP:
396 if (!init_pip(width, height))
397 return false;
398 break;
399
400 case DISPLAY_SCREEN_P96:
401 if (!init_screen_p96(mode_id))
402 return false;
403 break;
404
405 case DISPLAY_SCREEN_CGFX:
406 if (!init_screen_cgfx(mode_id))
407 return false;
408 break;
409 }
410
411 // Start periodic process
412 periodic_proc = CreateNewProcTags(
413 NP_Entry, (ULONG)periodic_func,
414 NP_Name, (ULONG)"Basilisk II IDCMP Handler",
415 NP_Priority, 0,
416 TAG_END
417 );
418 if (periodic_proc == NULL) {
419 ErrorAlert(GetString(STR_NO_MEM_ERR));
420 return false;
421 }
422 return true;
423 }
424
425
426 /*
427 * Deinitialization
428 */
429
430 void VideoExit(void)
431 {
432 // Stop periodic process
433 if (periodic_proc) {
434 SetSignal(0, SIGF_SINGLE);
435 Signal(&periodic_proc->pr_Task, SIGBREAKF_CTRL_C);
436 Wait(SIGF_SINGLE);
437 }
438
439 switch (display_type) {
440
441 case DISPLAY_WINDOW:
442
443 // Window mode, free bitmap
444 if (the_bitmap) {
445 WaitBlit();
446 FreeBitMap(the_bitmap);
447 }
448
449 // Free pens and close window
450 if (the_win) {
451 ReleasePen(the_win->WScreen->ViewPort.ColorMap, black_pen);
452 ReleasePen(the_win->WScreen->ViewPort.ColorMap, white_pen);
453
454 CloseWindow(the_win);
455 }
456 break;
457
458 case DISPLAY_PIP:
459
460 // Close PIP
461 if (the_win)
462 p96PIP_Close(the_win);
463 break;
464
465 case DISPLAY_SCREEN_P96:
466
467 // Close window
468 if (the_win)
469 CloseWindow(the_win);
470
471 // Close screen
472 if (the_screen) {
473 p96CloseScreen(the_screen);
474 the_screen = NULL;
475 }
476 break;
477
478 case DISPLAY_SCREEN_CGFX:
479
480 // Close window
481 if (the_win)
482 CloseWindow(the_win);
483
484 // Close screen
485 if (the_screen) {
486 CloseScreen(the_screen);
487 the_screen = NULL;
488 }
489 break;
490 }
491 }
492
493
494 /*
495 * Set palette
496 */
497
498 void video_set_palette(uint8 *pal)
499 {
500 if (display_type == DISPLAY_SCREEN_P96 || display_type == DISPLAY_SCREEN_CGFX) {
501
502 // Convert palette to 32 bits
503 ULONG table[2 + 256 * 3];
504 table[0] = 256 << 16;
505 table[256 * 3 + 1] = 0;
506 for (int i=0; i<256; i++) {
507 table[i*3+1] = pal[i*3] * 0x01010101;
508 table[i*3+2] = pal[i*3+1] * 0x01010101;
509 table[i*3+3] = pal[i*3+2] * 0x01010101;
510 }
511
512 // And load it
513 LoadRGB32(&the_screen->ViewPort, table);
514 }
515 }
516
517
518 /*
519 * Video message handling (not neccessary under AmigaOS, handled by periodic_func())
520 */
521
522 void VideoInterrupt(void)
523 {
524 }
525
526
527 /*
528 * Process for window refresh and message handling
529 */
530
531 static __saveds void periodic_func(void)
532 {
533 struct MsgPort *timer_port = NULL;
534 struct timerequest *timer_io = NULL;
535 struct IntuiMessage *msg;
536 ULONG win_mask = 0, timer_mask = 0;
537
538 // Create message port for window and attach it
539 struct MsgPort *win_port = CreateMsgPort();
540 if (win_port) {
541 win_mask = 1 << win_port->mp_SigBit;
542 the_win->UserPort = win_port;
543 ModifyIDCMP(the_win, IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE | IDCMP_RAWKEY | ((display_type == DISPLAY_SCREEN_P96 || display_type == DISPLAY_SCREEN_CGFX) ? IDCMP_DELTAMOVE : 0));
544 }
545
546 // Start 60Hz timer for window refresh
547 if (display_type == DISPLAY_WINDOW) {
548 timer_port = CreateMsgPort();
549 if (timer_port) {
550 timer_io = (struct timerequest *)CreateIORequest(timer_port, sizeof(struct timerequest));
551 if (timer_io) {
552 if (!OpenDevice((UBYTE *)TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0)) {
553 timer_mask = 1 << timer_port->mp_SigBit;
554 timer_io->tr_node.io_Command = TR_ADDREQUEST;
555 timer_io->tr_time.tv_secs = 0;
556 timer_io->tr_time.tv_micro = 16667 * frame_skip;
557 SendIO((struct IORequest *)timer_io);
558 }
559 }
560 }
561 }
562
563 // Main loop
564 for (;;) {
565
566 // Wait for timer and/or window (CTRL_C is used for quitting the task)
567 ULONG sig = Wait(win_mask | timer_mask | SIGBREAKF_CTRL_C);
568
569 if (sig & SIGBREAKF_CTRL_C)
570 break;
571
572 if (sig & timer_mask) {
573
574 // Timer tick, update display
575 BltTemplate(the_bitmap->Planes[0], 0, the_bitmap->BytesPerRow, the_win->RPort,
576 the_win->BorderLeft, the_win->BorderTop, VideoMonitor.x, VideoMonitor.y);
577
578 // Restart timer
579 timer_io->tr_node.io_Command = TR_ADDREQUEST;
580 timer_io->tr_time.tv_secs = 0;
581 timer_io->tr_time.tv_micro = 16667 * frame_skip;
582 SendIO((struct IORequest *)timer_io);
583 }
584
585 if (sig & win_mask) {
586
587 // Handle window messages
588 while (msg = (struct IntuiMessage *)GetMsg(win_port)) {
589
590 // Get data from message and reply
591 ULONG cl = msg->Class;
592 UWORD code = msg->Code;
593 UWORD qualifier = msg->Qualifier;
594 WORD mx = msg->MouseX;
595 WORD my = msg->MouseY;
596 ReplyMsg((struct Message *)msg);
597
598 // Handle message according to class
599 switch (cl) {
600 case IDCMP_MOUSEMOVE:
601 if (display_type == DISPLAY_SCREEN_P96 || display_type == DISPLAY_SCREEN_CGFX)
602 ADBMouseMoved(mx, my);
603 else
604 ADBMouseMoved(mx - the_win->BorderLeft, my - the_win->BorderTop);
605 break;
606
607 case IDCMP_MOUSEBUTTONS:
608 if (code == SELECTDOWN)
609 ADBMouseDown(0);
610 else if (code == SELECTUP)
611 ADBMouseUp(0);
612 else if (code == MENUDOWN)
613 ADBMouseDown(1);
614 else if (code == MENUUP)
615 ADBMouseUp(1);
616 else if (code == MIDDLEDOWN)
617 ADBMouseDown(2);
618 else if (code == MIDDLEUP)
619 ADBMouseUp(2);
620 break;
621
622 case IDCMP_RAWKEY:
623 if (qualifier & IEQUALIFIER_REPEAT) // Keyboard repeat is done by MacOS
624 break;
625 if (code & IECODE_UP_PREFIX)
626 ADBKeyUp(keycode2mac[code & 0x7f]);
627 else
628 ADBKeyDown(keycode2mac[code & 0x7f]);
629 break;
630 }
631 }
632 }
633 }
634
635 // Stop timer
636 if (timer_io) {
637 if (!CheckIO((struct IORequest *)timer_io))
638 AbortIO((struct IORequest *)timer_io);
639 WaitIO((struct IORequest *)timer_io);
640 CloseDevice((struct IORequest *)timer_io);
641 DeleteIORequest(timer_io);
642 }
643 if (timer_port)
644 DeleteMsgPort(timer_port);
645
646 // Remove port from window and delete it
647 Forbid();
648 msg = (struct IntuiMessage *)win_port->mp_MsgList.lh_Head;
649 struct Node *succ;
650 while (succ = msg->ExecMessage.mn_Node.ln_Succ) {
651 if (msg->IDCMPWindow == the_win) {
652 Remove((struct Node *)msg);
653 ReplyMsg((struct Message *)msg);
654 }
655 msg = (struct IntuiMessage *)succ;
656 }
657 the_win->UserPort = NULL;
658 ModifyIDCMP(the_win, 0);
659 Permit();
660 DeleteMsgPort(win_port);
661
662 // Main task asked for termination, send signal
663 Forbid();
664 Signal(MainTask, SIGF_SINGLE);
665 }