2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
5 Desc: Code for CONU_CHARMAP console units.
10 #include <exec/ports.h>
11 #include <proto/graphics.h>
12 #include <proto/intuition.h>
13 #include <proto/alib.h>
14 #include <intuition/intuition.h>
16 #include <intuition/imageclass.h>
17 #include <intuition/gadgetclass.h>
18 #include <intuition/sghooks.h>
19 #include <libraries/gadtools.h>
21 #include <graphics/rastport.h>
22 #include <aros/asmcall.h>
29 #include <aros/debug.h>
31 #include "console_gcc.h"
32 #include "consoleif.h"
37 AUTOKNOB | FREEVERT | PROPNEWLOOK | PROPBORDERLESS
40 #define CODE_PASTE 'V'
49 static const STRPTR CONCLIP_PORTNAME
= "ConClip.rendezvous";
52 struct Gadget scroller
; /* proportionnal gadget */
53 struct Gadget down
; /* down gadget */
54 struct Gadget up
; /* up gadget */
55 struct PropInfo pinfo
; /* PropInfo for scroller */
56 struct Image simage
; /* image for scroller */
57 struct Image
* upimage
; /* Boopsi image for up arrow */
58 struct Image
* downimage
; /* ditto for down arrow */
61 // FIXME: Abstract out the non-GUI aspects
64 /* Start of the scrollback buffer */
65 struct charmap_line
* top_of_scrollback
;
66 /* The line currently displayed at the top of the screen */
67 struct charmap_line
* top_of_window
;
68 /* Saved position for the top of the screen at the end of
69 the buffer; where the buffer is reset to if there is
70 output while scrolling */
71 struct charmap_line
* saved_top_of_window
;
72 ULONG saved_scrollback_pos
;
74 ULONG scrollback_size
; /* Total size of the scrollback buffer */
75 ULONG scrollback_pos
; /* Position of the top of the window */
77 ULONG scrollback_max
; /* Maximum number of lines in scrollback
78 buffer on top of CHAR_YMAX(o) */
80 BOOL unrendered
; /* Unrendered cursor while scrolled back? */
82 /* FIXME: Belongs in snipmap class */
83 /* Current selection */
86 struct charmap_line
* select_line_min
;
89 struct charmap_line
* select_line_max
;
90 BOOL active_selection
; /* If true, mouse move will affect the selection */
94 UBYTE boopsigad
; /* Type of right prop gadget of window */
101 struct Library
*ccd_GfxBase
;
105 /* Template used to quickly fill constant fields */
106 CONST
struct Scroll ScrollBar
= {
109 GFLG_RELRIGHT
|GFLG_RELHEIGHT
,
110 GACT_RIGHTBORDER
|GACT_FOLLOWMOUSE
|GACT_IMMEDIATE
|GACT_RELVERIFY
,
112 NULL
, NULL
, NULL
, 0, NULL
,
116 GFLG_RELRIGHT
|GFLG_RELBOTTOM
|GFLG_GADGHIMAGE
|GFLG_GADGIMAGE
,
117 GACT_RIGHTBORDER
|GACT_RELVERIFY
|GACT_IMMEDIATE
,
119 NULL
, NULL
, NULL
, 0, NULL
,
123 GFLG_RELRIGHT
|GFLG_RELBOTTOM
|GFLG_GADGHIMAGE
|GFLG_GADGIMAGE
,
124 GACT_RIGHTBORDER
|GACT_RELVERIFY
|GACT_IMMEDIATE
,
126 NULL
, NULL
, NULL
, 0, NULL
,
132 /* Other values may be NULL */
137 #define ConsoleDevice ((struct ConsoleBase *)cl->cl_UserData)
140 #define GfxBase (((struct charmapcondata *)INST_DATA(cl, o))->ccd_GfxBase)
142 static VOID
charmapcon_refresh(Class
*cl
, Object
* o
, LONG off
);
144 /*** Allocate and attach a prop gadget to the window ***/
145 static VOID
charmapcon_add_prop(Class
* cl
, Object
* o
)
147 struct charmapcondata
*data
= INST_DATA(cl
, o
);
150 struct Window
*win
= CU(o
)->cu_Window
;
152 UWORD height
, size_width
, size_height
;
154 /* If the window is a backdrop'ed one, use a simplified BOOPSI propgadget **
155 ** because the next propgadget aspect depends on window activated state */
156 if (win
->Flags
& WFLG_BACKDROP
)
158 /* Yes this is actually a (struct Gadget *)... */
159 if ((data
->prop
= pg
= (struct Scroll
*)NewObject(NULL
, "propgclass",
161 GA_Left
, win
->Width
- 10,
163 GA_Height
, win
->Height
,
165 GA_FollowMouse
, TRUE
,
168 PGA_VertBody
, MAXBODY
,
169 PGA_Freedom
, FREEVERT
,
173 /* And finally, add it to the window */
174 AddGList(win
, (struct Gadget
*)pg
, 0, 1, NULL
);
175 RefreshGList((struct Gadget
*)pg
, win
, NULL
, 1);
178 data
->boopsigad
= TRUE
;
181 data
->boopsigad
= FALSE
;
184 if( ( data
->prop
= pg
= (void *) AllocMem(sizeof(*pg
), MEMF_PUBLIC
) ) )
186 /* Copy default flags/modes/etc. */
187 CopyMem(&ScrollBar
, pg
, sizeof(*pg
));
188 pg
->pinfo
.Flags
= PROP_FLAGS
;
191 di
= (void *) GetScreenDrawInfo(win
->WScreen
);
193 /* We need to get size-gadget height, to adjust properly arrows */
194 if((dummy
= (struct Image
*) NewObject(NULL
, "sysiclass",
195 SYSIA_Which
, SIZEIMAGE
,
196 SYSIA_DrawInfo
, (IPTR
)di
,
199 size_width
= dummy
->Width
; /* width of up/down-gadgets */
200 size_height
= dummy
->Height
; /* bottom offset */
202 /* We don't need the image anymore */
203 DisposeObject(dummy
);
205 /* Get the boopsi image of the up and down arrow */
206 if((pg
->upimage
= (struct Image
*) NewObject(NULL
, "sysiclass",
207 SYSIA_Which
, UPIMAGE
,
208 SYSIA_DrawInfo
, (IPTR
)di
,
211 pg
->up
.GadgetRender
= pg
->up
.SelectRender
= (APTR
)pg
->upimage
;
212 height
= pg
->upimage
->Height
;
214 if((pg
->downimage
= (struct Image
*) NewObject(NULL
, "sysiclass",
215 SYSIA_Which
, DOWNIMAGE
,
216 SYSIA_DrawInfo
, (IPTR
)di
,
219 struct Gadget
*G
= (void *)pg
;
220 WORD hoffset
= size_width
/ 4;
222 pg
->down
.GadgetRender
= pg
->down
.SelectRender
= (APTR
)pg
->downimage
;
224 /* Release drawinfo */
225 FreeScreenDrawInfo (win
->WScreen
, di
);
227 /* Now init all sizes/positions relative to window's borders */
228 G
->Height
= -(win
->BorderTop
+ size_height
+ 2*height
+ 2);
229 G
->TopEdge
= win
->BorderTop
+ 1;
230 G
->Width
= size_width
- hoffset
* 2 + 2;
231 G
->LeftEdge
= -(size_width
- hoffset
); G
++;
233 G
->LeftEdge
= -(size_width
- 1);
234 G
->Width
= pg
->up
.Width
= size_width
;
235 G
->Height
= pg
->up
.Height
= height
;
236 G
->TopEdge
= -(size_height
+ height
- 1);
237 pg
->up
.TopEdge
= G
->TopEdge
- height
;
240 pg
->scroller
.GadgetRender
= (APTR
)&pg
->simage
;
241 pg
->scroller
.SpecialInfo
= (APTR
)&pg
->pinfo
;
244 pg
->scroller
.NextGadget
= &pg
->up
;
245 pg
->up
.NextGadget
= &pg
->down
;
247 /* And finally, add them to the window */
248 AddGList(win
, &pg
->scroller
, 0, 3, NULL
);
249 RefreshGList(&pg
->scroller
, win
, NULL
, 3);
253 DisposeObject(pg
->upimage
);
256 FreeMem(pg
, sizeof(*pg
));
257 FreeScreenDrawInfo(win
->WScreen
, di
);
262 static VOID
charmapcon_adj_prop(Class
*cl
, Object
*o
)
264 struct charmapcondata
*data
= INST_DATA(cl
, o
);
265 struct Window
*w
= CU(o
)->cu_Window
;
266 ULONG VertBody
, VertPot
;
268 ULONG hidden
= data
->scrollback_size
> CHAR_YMAX(o
) ? data
->scrollback_size
- CHAR_YMAX(o
) - 1 : 0;
271 VertPot
= (data
->scrollback_pos
) * MAXPOT
/ hidden
;
272 VertBody
= CHAR_YMAX(o
) * MAXBODY
/ data
->scrollback_size
;
278 if (VertPot
> MAXPOT
) {
280 D(bug("VERTPOT SET TOO HIGH. Adjusted\n"));
283 NewModifyProp((struct Gadget
*)&(data
->prop
->scroller
),w
,NULL
,((struct PropInfo
*)data
->prop
->scroller
.SpecialInfo
)->Flags
,MAXPOT
,VertPot
,MAXBODY
,VertBody
,1);
286 /*** Free resources allocated for scroller ***/
287 void charmapcon_free_prop(Class
* cl
, Object
* o
)
289 struct charmapcondata
*data
= INST_DATA(cl
, o
);
290 struct Window
* win
= CU(o
)->cu_Window
;
296 if (data
->boopsigad
) RemoveGadget(win
,(struct Gadget
*)data
->prop
);
297 else RemoveGList(win
,&data
->prop
->scroller
, 3);
299 if( data
->boopsigad
) DisposeObject( data
->prop
);
303 DisposeObject(data
->prop
->upimage
);
304 DisposeObject(data
->prop
->downimage
);
307 FreeMem(data
->prop
, sizeof(* data
->prop
));
314 /*********** CharMapCon::New() **********************/
316 static Object
*charmapcon_new(Class
*cl
, Object
*o
, struct opSet
*msg
)
318 EnterFunc(bug("CharMapCon::New()\n"));
319 APTR newGfxBase
= TaggedOpenLibrary(TAGGEDOPEN_GRAPHICS
);
320 if (newGfxBase
== NULL
)
323 o
= (Object
*)DoSuperMethodA(cl
, o
, (Msg
)msg
);
326 struct charmapcondata
*data
= INST_DATA(cl
, o
);
328 /* Clear for checking inside dispose() whether stuff was allocated.
329 Basically this is bug-prevention.
331 memset(data
, 0, sizeof (struct charmapcondata
));
333 data
->scrollback_max
= 500; /* FIXME: Don't hardcode it */
334 data
->ccd_GfxBase
= newGfxBase
;
335 charmapcon_add_prop(cl
,o
);
337 ReturnPtr("CharMapCon::New", Object
*, o
);
340 CloseLibrary(newGfxBase
);
341 ReturnPtr("CharMapCon::New", Object
*, NULL
);
345 /*********** CharMapCon::Dispose() **************************/
347 static VOID
charmapcon_dispose(Class
*cl
, Object
*o
, Msg msg
)
349 struct charmapcondata
*data
= INST_DATA(cl
, o
);
351 charmap_dispose_lines(data
->top_of_scrollback
);
352 charmapcon_free_prop(cl
,o
);
354 CloseLibrary(data
->ccd_GfxBase
);
356 DoSuperMethodA(cl
, o
, msg
);
359 /********* CharMapCon::DoCommand() ****************************/
361 static struct charmap_line
* charmapcon_find_line(Class
* cl
, Object
* o
, ULONG ycp
)
363 struct charmapcondata
*data
= INST_DATA(cl
, o
);
365 // Find the line. This is inefficient but the number of lines on screen
366 // should never be very high.
367 // FIXME: Optimizing the case of appending to the end (e.g. know what line
368 // is on the last line of the buffer).
370 struct charmap_line
* line
= data
->top_of_window
;
372 D(bug("Initializing charmap\n"));
373 data
->top_of_window
= data
->top_of_scrollback
= line
= charmap_newline(0,0);
374 data
->scrollback_size
= 1;
377 D(bug("Finding line %ld\n",ycp
));
380 charmap_newline(0,line
);
381 data
->scrollback_size
+= 1;
387 while (data
->scrollback_size
> data
->scrollback_max
+ CHAR_YMAX(o
) &&
388 data
->top_of_window
!= data
->top_of_scrollback
) {
389 data
->scrollback_size
-= 1;
390 data
->scrollback_pos
-= 1;
392 /* FIXME: Needs testing... */
393 if (data
->select_line_max
== data
->select_line_min
&&
394 data
->select_line_min
== data
->top_of_scrollback
)
396 /* The entire selection has scrolled out, but we keep it in case
399 data
->select_line_min
= data
->select_line_min
->next
;
400 data
->select_line_max
= data
->select_line_min
;
401 data
->select_x_min
= 0;
402 data
->select_x_max
= 0;
403 data
->select_y_min
+= 1;
404 data
->select_y_max
+= 1;
406 else if (data
->top_of_scrollback
== data
->select_line_min
)
408 data
->select_line_min
= data
->select_line_min
->next
;
409 data
->select_y_min
+= 1;
410 data
->select_x_min
= 0;
412 else if (data
->top_of_scrollback
== data
->select_line_max
)
414 /* "Reversed" selection */
415 data
->select_line_max
= data
->select_line_max
->next
;
416 data
->select_y_max
+= 1;
417 data
->select_x_max
= 0;
420 data
->top_of_scrollback
= charmap_dispose_line(data
->top_of_scrollback
);
426 static VOID
charmap_ascii(Class
* cl
, Object
* o
, ULONG xcp
, ULONG ycp
, char * str
, ULONG len
)
428 struct charmap_line
*line
= charmapcon_find_line(cl
,o
, ycp
);
429 ULONG oldsize
= line
->size
;
431 // Ensure the line has sufficient capacity.
432 if (line
->size
< xcp
+ len
) charmap_resize(line
, xcp
+ len
);
434 // .. copy the required data
435 memset(line
->fgpen
+ xcp
, CU(o
)->cu_FgPen
, len
);
436 memset(line
->bgpen
+ xcp
, CU(o
)->cu_BgPen
, len
);
437 memset(line
->flags
+ xcp
, CU(o
)->cu_TxFlags
, len
);
438 memcpy(line
->text
+ xcp
, str
, len
);
440 // If cursor output is moved further right on the screen than
441 // the last output, we need to fill the line
443 memset(line
->fgpen
+ oldsize
, CU(o
)->cu_FgPen
, xcp
- oldsize
);
444 memset(line
->bgpen
+ oldsize
, CU(o
)->cu_BgPen
, xcp
- oldsize
);
445 memset(line
->flags
+ oldsize
, CU(o
)->cu_TxFlags
, xcp
- oldsize
);
446 memset(line
->text
+ oldsize
, ' ', xcp
- oldsize
);
450 static VOID
charmap_scroll_up(Class
* cl
, Object
* o
, ULONG y
)
452 struct charmapcondata
*data
= INST_DATA(cl
, o
);
454 if (!data
->top_of_window
) return;
457 if (!data
->top_of_window
->next
) {
458 charmap_newline(0, data
->top_of_window
);
459 data
->scrollback_size
+= 1;
461 data
->top_of_window
= data
->top_of_window
->next
;
462 data
->scrollback_pos
+= 1;
463 data
->select_y_max
-= 1;
464 data
->select_y_min
-= 1;
467 if (data
->scrollback_size
- CHAR_YMAX(o
) - 1 <= data
->scrollback_pos
&&
469 Console_RenderCursor(o
);
470 data
->unrendered
= 0;
473 while (data
->scrollback_size
> data
->scrollback_max
+ CHAR_YMAX(o
) &&
474 data
->top_of_window
!= data
->top_of_scrollback
) {
475 data
->scrollback_size
-= 1;
476 data
->scrollback_pos
-= 1;
477 data
->top_of_scrollback
= charmap_dispose_line(data
->top_of_scrollback
);
481 static VOID
charmap_scroll_down(Class
* cl
, Object
* o
, ULONG y
)
483 // FIXME: Need to adjust cursor position or reset to bottom when editing.
484 struct charmapcondata
*data
= INST_DATA(cl
, o
);
485 if (!data
->unrendered
) {
486 Console_UnRenderCursor(o
);
487 data
->unrendered
= 1;
488 data
->saved_scrollback_pos
= data
->scrollback_pos
;
489 data
->saved_top_of_window
= data
->top_of_window
;
491 // FIXME: Select position.
493 if (data
->top_of_window
) {
494 while(y
-- && data
->top_of_window
->prev
) {
495 data
->top_of_window
= data
->top_of_window
->prev
;
496 data
->scrollback_pos
-= 1;
497 data
->select_y_max
+= 1;
498 data
->select_y_min
+= 1;
503 static VOID
charmapcon_scroll_to(Class
* cl
, Object
* o
, ULONG y
)
505 struct charmapcondata
*data
= INST_DATA(cl
, o
);
506 struct Window
*w
= CU(o
)->cu_Window
;
507 struct RastPort
*rp
= w
->RPort
;
508 LONG off
= data
->scrollback_pos
- y
;
509 LONG old_pos
= data
->scrollback_pos
;
511 if (off
== 0) return;
513 Console_UnRenderCursor(o
);
515 if (off
> 0) charmap_scroll_down(cl
, o
, off
);
516 else charmap_scroll_up(cl
,o
,-off
);
518 /* Correct offset to account for the fact we might reach the
519 * top or bottom of the buffer:
521 off
= old_pos
- data
->scrollback_pos
;
523 /* A whole screenful? If so we have no choice but a full refresh
524 * (though we could double buffer... Not sure that's worth the
527 if (abs(off
) > CHAR_YMAX(o
))
529 charmapcon_refresh(cl
,o
,0);
530 Console_RenderCursor(o
);
534 /* Avoid a full refresh by scrolling the rastport */
535 SetBPen( rp
, CU(o
)->cu_BgPen
); /* Use "standard" background to reduce flicker */
558 /* Partial refresh */
559 charmapcon_refresh(cl
,o
,off
);
561 Console_RenderCursor(o
);
565 static VOID
charmap_delete_char(Class
* cl
, Object
*o
, ULONG x
, ULONG y
)
567 struct charmap_line
* line
= charmapcon_find_line(cl
,o
, y
);
569 if (!line
|| x
>= line
->size
) return;
571 // FIXME: Shrink the buffer, or keep track of capacity separately.
572 if (x
+ 1 >= line
->size
) {
577 memmove(line
->fgpen
+ x
, line
->fgpen
+ x
+ 1, 1);
578 memmove(line
->bgpen
+ x
, line
->bgpen
+ x
+ 1, 1);
579 memmove(line
->flags
+ x
, line
->flags
+ x
+ 1, 1);
580 memmove(line
->text
+ x
, line
->text
+ x
+ 1, 1);
583 static VOID
charmap_insert_char(Class
* cl
, Object
*o
, ULONG x
, ULONG y
)
585 struct charmap_line
* line
= charmapcon_find_line(cl
,o
, y
);
587 if (x
>= line
->size
) return;
589 /* FIXME: This is wasteful, since it copies the buffers straight over,
590 * so we have to do memmove's further down. */
591 charmap_resize(line
, line
->size
+ 1);
593 memmove(line
->fgpen
+ x
+ 1, line
->fgpen
+ x
, line
->size
- x
- 1);
594 memmove(line
->bgpen
+ x
+ 1, line
->bgpen
+ x
, line
->size
- x
- 1);
595 memmove(line
->flags
+ x
+ 1, line
->flags
+ x
, line
->size
- x
- 1);
596 memmove(line
->text
+ x
+ 1, line
->text
+ x
, line
->size
- x
- 1);
598 line
->fgpen
[x
] = CU(o
)->cu_FgPen
;
599 line
->bgpen
[x
] = CU(o
)->cu_BgPen
;
600 line
->flags
[x
] = CU(o
)->cu_TxFlags
;
604 static VOID
charmap_formfeed(Class
*cl
, Object
* o
)
606 struct charmapcondata
*data
= INST_DATA(cl
, o
);
607 struct charmap_line
* line
= data
->top_of_window
;
610 charmap_resize(line
,0);
615 static VOID
charmapcon_docommand(Class
*cl
, Object
*o
, struct P_Console_DoCommand
*msg
)
618 IPTR
*params
= msg
->Params
;
619 struct charmapcondata
*data
= INST_DATA(cl
, o
);
621 EnterFunc(bug("CharMapCon::DoCommand(o=%p, cmd=%d, params=%p) x=%d, y=%d, ymax=%d\n",
622 o
, msg
->Command
, params
,XCP
,YCP
, CHAR_YMAX(o
)));
624 // This is a bit of a hack: Set position to bottom in order to prevent output while
627 ULONG old_scrollback_size
= data
->scrollback_size
;
628 ULONG old_scrollback_pos
= data
->scrollback_pos
;
630 if (data
->unrendered
) {
631 data
->unrendered
= 0;
632 data
->scrollback_pos
= data
->saved_scrollback_pos
;
633 data
->select_y_min
+= old_scrollback_pos
- data
->scrollback_pos
;
634 data
->select_y_max
+= old_scrollback_pos
- data
->scrollback_pos
;
635 data
->top_of_window
= data
->saved_top_of_window
;
636 charmapcon_refresh(cl
,o
,0);
637 Console_RenderCursor(o
);
640 switch (msg
->Command
)
643 charmap_ascii(cl
,o
, XCP
, YCP
, (char *)¶ms
[0],1);
644 DoSuperMethodA(cl
, o
, (Msg
)msg
);
648 charmap_ascii(cl
,o
, XCP
, YCP
, (char *)params
[0], (int)params
[1]);
649 DoSuperMethodA(cl
, o
, (Msg
)msg
);
653 charmap_formfeed(cl
,o
);
654 DoSuperMethodA(cl
, o
, (Msg
)msg
);
657 case C_DELETE_CHAR
: /* FIXME: can it have params!? */
658 charmap_delete_char(cl
,o
, XCP
,YCP
);
659 DoSuperMethodA(cl
, o
, (Msg
)msg
);
663 charmap_insert_char(cl
,o
, XCP
,YCP
);
664 DoSuperMethodA(cl
, o
, (Msg
)msg
);
669 D(bug("C_SCROLL_UP area (%d, %d) to (%d, %d), %d\n",
670 GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
), GFX_YMAX(o
), YRSIZE
* params
[0]));
671 charmap_scroll_up(cl
,o
, params
[0]);
672 DoSuperMethodA(cl
, o
, (Msg
)msg
);
678 D(bug("C_SCROLL_DOWN area (%d, %d) to (%d, %d), %d\n",
679 GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
), GFX_YMAX(o
), YRSIZE
* params
[0]));
680 charmap_scroll_down(cl
,o
, params
[0]);
681 DoSuperMethodA(cl
, o
, (Msg
)msg
);
686 DoSuperMethodA(cl
, o
, (Msg
)msg
);
690 if (old_scrollback_size
!= data
->scrollback_size
||
691 old_scrollback_pos
!= data
->scrollback_pos
)
692 charmapcon_adj_prop(cl
,o
);
694 ReturnVoid("CharMapCon::DoCommand");
697 /**************************
698 ** CharMapCon::ClearCell() **
699 **************************/
700 static VOID
charmapcon_clearcell(Class
*cl
, Object
*o
, struct P_Console_ClearCell
*msg
)
702 // FIXME, insert space.
703 DoSuperMethodA(cl
, o
, (Msg
)msg
);
706 static VOID
charmapcon_refresh_lines(Class
*cl
, Object
* o
, LONG fromLine
, LONG toLine
)
708 struct Window
*w
= CU(o
)->cu_Window
;
709 struct RastPort
*rp
= w
->RPort
;
710 struct charmapcondata
*data
= INST_DATA(cl
, o
);
712 if (fromLine
< CHAR_YMIN(o
)) fromLine
= CHAR_YMIN(o
);
713 if (toLine
> CHAR_YMAX(o
)) toLine
= CHAR_YMAX(o
);
715 D(bug("fromLine: %ld, toLine: %ld, char_ymax: %ld\n",fromLine
,toLine
, CHAR_YMAX(o
)));
717 if (toLine
< fromLine
) return;
719 Console_UnRenderCursor(o
);
721 D(bug("Rendering charmap\n"));
723 struct charmap_line
* line
= charmapcon_find_line(cl
,o
, fromLine
);
724 ULONG y
= GFX_YMIN(o
)+fromLine
*YRSIZE
+rp
->Font
->tf_Baseline
;
728 LONG selectstart_x
,selectstart_y
,selectend_x
,selectend_y
;
729 if (data
->select_y_min
== data
->select_y_max
)
731 selectstart_y
= selectend_y
= data
->select_y_min
;
732 selectstart_x
= MIN(data
->select_x_min
,data
->select_x_max
);
733 selectend_x
= MAX(data
->select_x_min
,data
->select_x_max
);
735 else if (data
->select_y_min
< data
->select_y_max
)
737 selectstart_y
= data
->select_y_min
;
738 selectstart_x
= data
->select_x_min
;
739 selectend_y
= data
->select_y_max
;
740 selectend_x
= data
->select_x_max
;
744 selectstart_y
= data
->select_y_max
;
745 selectstart_x
= data
->select_x_max
;
746 selectend_y
= data
->select_y_min
;
747 selectend_x
= data
->select_x_min
;
750 while(line
&& yc
<= toLine
)
752 const char * str
= line
->text
;
754 ULONG remaining_space
= CHAR_XMAX(o
)+1;
755 Move(rp
, GFX_XMIN(o
), y
);
756 while (line
->size
> start
&& remaining_space
> 0 && str
[start
])
758 /* Identify a batch of characters with the same fgpen/bgpen
759 to avoid having to move/set pens and do Text() on single
762 UBYTE fgpen
= line
->fgpen
[start
];
763 UBYTE bgpen
= line
->bgpen
[start
];
765 /* Is any part of this line part of a selection?
766 * If so, we bake in a state transition on "stop".
767 * This code is messy - there must be a nicer way.
769 ULONG stop
= 9999999;
770 BOOL in_selection
= 0;
772 if (yc
> selectstart_y
&& yc
< selectend_y
)
776 else if (yc
== selectstart_y
)
778 if (yc
== selectend_y
)
780 if (start
>= selectstart_x
&&
786 else if (start
< selectstart_x
)
788 stop
= selectstart_x
;
793 if (start
>= selectstart_x
) in_selection
= 1;
794 else stop
= selectstart_x
;
797 else if (yc
== selectend_y
)
799 /* In this case, the selection *ends* on selectend_x */
800 if (start
< selectend_x
) {
806 if (stop
== start
) stop
+= 1;
809 fgpen
= line
->bgpen
[start
];
810 bgpen
= line
->fgpen
[start
];
814 while (line
->size
> start
+ len
&& str
[start
+ len
] &&
815 len
< remaining_space
&&
816 line
->fgpen
[start
] == line
->fgpen
[start
+ len
] &&
817 line
->bgpen
[start
] == line
->bgpen
[start
+ len
] &&
818 line
->flags
[start
] == line
->flags
[start
+ len
] &&
819 start
+ len
< stop
) len
+= 1;
821 setabpen(GfxBase
, rp
, line
->flags
[start
], fgpen
, bgpen
);
822 if ((line
->flags
[start
] & CON_TXTFLAGS_MASK
) != (flags
& CON_TXTFLAGS_MASK
)) {
823 SetSoftStyle(rp
, line
->flags
[start
], CON_TXTFLAGS_MASK
);
825 flags
= line
->flags
[start
];
827 Text(rp
,&str
[start
],len
);
830 remaining_space
-= len
;
833 /* Clear to EOL, without overwriting scroll bar (ClearEOL does) */
834 SetAPen( rp
, CU(o
)->cu_BgPen
);
836 GFX_X(o
,start
), GFX_Y(o
,yc
),
837 GFX_XMAX(o
), GFX_Y(o
,yc
+1)-1);
841 /* We want to make sure we have lines covering the window once
842 there's something to scroll back, as that simplies resize handling
845 if (!line
->next
&& yc
<= toLine
) {
846 line
->next
= charmap_newline(0,line
);
847 data
->scrollback_size
+= 1;
854 SetAPen( rp
, CU(o
)->cu_BgPen
);
856 GFX_XMIN(o
), GFX_Y(o
,yc
),
857 GFX_XMAX(o
), GFX_Y(o
,toLine
));
860 Console_RenderCursor(o
);
865 * Refresh the full console unless "off" is provided.
866 * If off is set to a positive value, it indicates the
867 * number of rows from the top we start rendering.
868 * If off is set to a negative value, it indicates the
869 * number of rows from the top we stop rendering.
870 * This is used for partial refreshes when the screen is
873 static VOID
charmapcon_refresh(Class
*cl
, Object
* o
, LONG off
)
876 LONG toLine
= CHAR_YMAX(o
) - CHAR_YMIN(o
);
878 if (off
> 0) toLine
= off
-1;
879 if (off
< 0) fromLine
= CHAR_YMAX(o
)+off
+1;
881 charmapcon_refresh_lines(cl
,o
,fromLine
,toLine
);
885 static ULONG
charmapcon_calc_selection_size(struct charmap_line
* first
,
886 struct charmap_line
* last
,
887 ULONG minx
, ULONG maxx
)
889 struct charmap_line
* cur
= first
;
894 if (cur
== last
) size
+= abs(maxx
- minx
);
895 else size
+= cur
->size
- minx
;
896 } else if (cur
== last
) {
902 if (cur
!= last
|| (cur
== last
&& maxx
== cur
->size
)) size
+= 1; /* line feed */
904 if (cur
== last
) break;
910 char * charmapcon_get_selection(ULONG size
,
911 struct charmap_line
* first
,
912 struct charmap_line
* last
,
915 char * buf
= AllocMem(size
, MEMF_ANY
);
917 struct charmap_line
* cur
= first
;
919 if (!bufpos
) return 0;
922 if (cur
->text
) { /* empty lines may not have text ptrs */
925 CopyMem(cur
->text
+ MIN(minx
,maxx
),bufpos
, abs(maxx
- minx
));
926 bufpos
+= abs(maxx
-minx
);
928 CopyMem(cur
->text
+ minx
, bufpos
, cur
->size
- minx
);
929 bufpos
+= cur
->size
- minx
;
931 } else if (cur
== last
) {
932 CopyMem(cur
->text
,bufpos
, maxx
);
935 CopyMem(cur
->text
,bufpos
, cur
->size
);
939 if (cur
!= last
|| (cur
== last
&& maxx
== cur
->size
)) /* line feed */
944 if (cur
== last
) break;
952 ULONG flags
; /* always zero? */
953 ULONG size
; /* does not include NUL termination */
954 APTR buffer
; /* NUL-terminated string! */
957 /* FIXME: Belongs in snipmapcon - here temporary until refactored out
959 static VOID
charmapcon_copy(Class
*cl
, Object
*o
, Msg copymsg
)
961 struct charmapcondata
*data
= INST_DATA(cl
, o
);
962 struct MsgPort replyport
, *port
;
964 struct MyEditHookMsg msg
;
967 struct charmap_line
* first
, *last
;
968 ULONG minx
,maxx
,size
;
970 /* Create a string from the contents of the scrollback buffer */
971 if (data
->select_y_min
< data
->select_y_max
) {
972 first
= data
->select_line_min
;
973 last
= data
->select_line_max
;
974 minx
= data
->select_x_min
;
975 maxx
= data
->select_x_max
;
977 first
= data
->select_line_max
;
978 last
= data
->select_line_min
;
979 minx
= data
->select_x_max
;
980 maxx
= data
->select_x_min
;
983 size
= charmapcon_calc_selection_size(first
,last
,minx
,maxx
);
984 buf
= charmapcon_get_selection(size
,first
,last
,minx
,maxx
);
985 D(bug("%d bytes copied\n"));
987 /* If Conclip is running, we prefer using that */
988 if (IsListEmpty(&ConsoleDevice
->sniphooks
) && (port
= FindPort(CONCLIP_PORTNAME
))) {
989 /* AROS conclip format */
990 D(bug("AROS conclip\n"));
991 replyport
.mp_Node
.ln_Type
= NT_MSGPORT
;
992 replyport
.mp_Node
.ln_Name
= NULL
;
993 replyport
.mp_Node
.ln_Pri
= 0;
994 replyport
.mp_Flags
= PA_SIGNAL
;
995 replyport
.mp_SigBit
= SIGB_SINGLE
;
996 replyport
.mp_SigTask
= FindTask(NULL
);
997 NewList(&replyport
.mp_MsgList
);
999 msg
.msg
.mn_Node
.ln_Type
= NT_MESSAGE
;
1000 msg
.msg
.mn_ReplyPort
= &replyport
;
1001 msg
.msg
.mn_Length
= sizeof(msg
);
1003 msg
.code
= CODE_COPY
;
1007 sgw
.WorkBuffer
= buf
;
1010 sgw
.Code
= CODE_COPY
;
1014 sgw
.EditOp
= EO_BIGCHANGE
;
1016 sgw
.NumChars
= size
;
1018 SetSignal(0, SIGF_SINGLE
);
1019 PutMsg(port
, &msg
.msg
);
1020 WaitPort(&replyport
);
1026 ObtainSemaphore(&ConsoleDevice
->copyBufferLock
);
1027 FreeMem((APTR
)ConsoleDevice
->copyBuffer
,ConsoleDevice
->copyBufferSize
);
1029 ConsoleDevice
->copyBuffer
= buf
;
1030 if (ConsoleDevice
->copyBuffer
)
1031 ConsoleDevice
->copyBufferSize
= size
;
1033 ConsoleDevice
->copyBufferSize
= 0;
1035 if (!IsListEmpty(&ConsoleDevice
->sniphooks
)) {
1036 /* OS2-3.x compatible conclip format */
1037 struct Hook
*conhook
;
1038 struct ConClipData ccd
;
1040 D(bug("AOS conclip\n"));
1041 if (ConsoleDevice
->copyBufferSize
) {
1043 ccd
.size
= ConsoleDevice
->copyBufferSize
;
1044 /* must be NUL-terminated */
1045 ccd
.buffer
= AllocVec(ccd
.size
+ 1, MEMF_CLEAR
);
1047 CopyMem(ConsoleDevice
->copyBuffer
, ccd
.buffer
, ccd
.size
);
1048 ForeachNode(&ConsoleDevice
->sniphooks
, conhook
) {
1049 D(bug("Calling AOS conclip hook %p\n", conhook
));
1050 CALLHOOKPKT(conhook
, NULL
, &ccd
);
1052 FreeVec(ccd
.buffer
);
1056 ReleaseSemaphore(&ConsoleDevice
->copyBufferLock
);
1060 /*******************************
1061 ** CharMapCon::NewWindowSize() **
1062 *******************************/
1063 static VOID
charmapcon_newwindowsize(Class
*cl
, Object
*o
, struct P_Console_NewWindowSize
*msg
)
1065 struct charmapcondata
*data
= INST_DATA(cl
, o
);
1069 DoSuperMethodA(cl
, o
, (Msg
)msg
);
1070 D(bug("CharMapCon::NewWindowSize(o=%p) x=%d, y=%d, ymax=%d\n",
1071 o
, XCP
,YCP
, CHAR_YMAX(o
)));
1073 // Is console empty? Unlikely, but anyway.
1074 if (!data
->top_of_window
) return;
1076 // Scroll up if new window size has forced the cursor up
1077 if (old_ycp
> CHAR_YMAX(o
)) {
1078 charmap_scroll_up(cl
,o
, old_ycp
- CHAR_YMAX(o
));
1081 charmapcon_refresh(cl
,o
,0);
1084 static VOID
charmapcon_handlemouse(Class
*cl
, Object
*o
, struct P_Console_HandleGadgets
*msg
)
1086 struct charmapcondata
*data
= INST_DATA(cl
, o
);
1087 struct Window
*w
= CU(o
)->cu_Window
;
1088 struct InputEvent
* e
= msg
->Event
;
1090 /* We have the following states:
1091 * - No active selection and no mouse button => ignore
1092 * - No active selection and left mouse button => Start selection
1093 * - Active selection and no mouse button => End selection
1094 * - Active selection and left mouse button => Update selection
1100 * Take window-relative mouse position.
1101 * The original code here failed when the screen was moved away from (0, 0) position.
1102 * x and y were unaware of shifted display (ie_X and ie_Y are raw physical coordinates,
1103 * and w->LeftEdge and w->TopEdge are screen-relative), and this caused lockups here.
1104 * TODO: Verify this intuition's behavior with AmigaOS3.1 using screentest program.
1107 x = e->ie_X - w->LeftEdge;
1108 y = e->ie_Y - w->TopEdge;
1113 /* Active selection */
1115 if (!(e
->ie_Qualifier
& IEQUALIFIER_LEFTBUTTON
))
1118 data
->active_selection
= 0;
1119 data
->ignore_drag
= 0;
1123 if (!data
->active_selection
)
1125 if (e
->ie_Qualifier
& IEQUALIFIER_LEFTBUTTON
&& !data
->ignore_drag
)
1127 /* Inside the console area? */
1128 if (x
>= GFX_XMIN(o
) && x
<= GFX_XMAX(o
) &&
1129 y
>= GFX_YMIN(o
) && y
<= GFX_YMAX(o
))
1131 D(bug("activated selection with x: %ld, y: %ld, xmin: %ld, ymin: %Ld, xmax: %ld, ymax: %ld\n",
1132 x
,y
,GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
), GFX_YMAX(o
)));
1134 /* We need to clear these lines
1136 LONG old_min_y
= MIN(data
->select_y_min
, data
->select_y_max
);
1137 LONG old_max_y
= MAX(data
->select_y_min
, data
->select_y_max
);
1139 /* Yes, so start selection */
1140 data
->active_selection
= 1;
1141 data
->ignore_drag
= 0;
1142 data
->select_x_min
= (x
- GFX_XMIN(o
)) / XRSIZE
;
1143 data
->select_y_min
= (y
- GFX_YMIN(o
)) / YRSIZE
;
1144 data
->select_line_min
= charmapcon_find_line(cl
,o
,data
->select_y_min
);
1146 data
->select_x_max
= data
->select_x_min
;
1147 data
->select_y_max
= data
->select_y_min
;
1151 /* FIXME: Determine exactly which lines are affected, as
1154 - If max_y has increased, render from old_max_y to new
1155 - If min_y has decreased, refresh from new min y to old
1156 - If both have changed, refresh in two batches.
1157 - If there's been a reversal of the relative positions of
1158 y_min/y_max, refresh the entire range.
1160 charmapcon_refresh_lines(cl
,o
,old_min_y
, old_max_y
);
1162 } else data
->ignore_drag
= 1;
1167 /* Update selection. */
1168 if (x
>= GFX_XMIN(o
) && x
<= GFX_XMAX(o
) &&
1169 y
>= GFX_YMIN(o
) && y
<= GFX_YMAX(o
))
1173 xmax
= data
->select_x_max
;
1174 ymax
= data
->select_y_max
;
1175 data
->select_x_max
= (x
- GFX_XMIN(o
)) / XRSIZE
;
1176 data
->select_y_max
= (y
- GFX_YMIN(o
)) / YRSIZE
;
1177 data
->select_line_max
= charmapcon_find_line(cl
,o
,data
->select_y_max
);
1178 /* FIXME: More intelligent refresh */
1179 if (xmax
!= data
->select_x_max
||
1180 ymax
!= data
->select_y_max
)
1182 charmapcon_refresh_lines(cl
,o
, MIN(ymax
,MIN(data
->select_y_min
, data
->select_y_max
)), MAX(ymax
,MAX(data
->select_y_min
,data
->select_y_max
)));
1187 /* FIXME: Outside the console area, we need to scroll the window
1188 and just update the selection based on that */
1193 static VOID
charmapcon_handlegadgets(Class
*cl
, Object
*o
, struct P_Console_HandleGadgets
*msg
)
1195 struct InputEvent
* e
= msg
->Event
;
1197 if (e
->ie_Class
== IECLASS_RAWMOUSE
) {
1198 charmapcon_handlemouse(cl
,o
,msg
);
1202 struct charmapcondata
*data
= INST_DATA(cl
, o
);
1204 if (e
->ie_Class
== IECLASS_GADGETUP
)
1206 data
->activeGad
= 0;
1210 if (e
->ie_Class
== IECLASS_GADGETDOWN
)
1212 /* We pass 0 from consoletask if the mouse wheel is being used */
1213 if ((IPTR
)e
->ie_EventAddress
== 1) data
->activeGad
= (APTR
)&(data
->prop
->up
);
1214 else if ((IPTR
)e
->ie_EventAddress
== 2) data
->activeGad
= (APTR
)&(data
->prop
->down
);
1215 else data
->activeGad
= e
->ie_EventAddress
;
1218 if (data
->activeGad
== (APTR
)&(data
->prop
->scroller
))
1220 ULONG hidden
= data
->scrollback_size
> CHAR_YMAX(o
) ? data
->scrollback_size
- CHAR_YMAX(o
) -1 : 0;
1221 ULONG pos
= (((struct PropInfo
*)((struct Gadget
*)&(data
->prop
->scroller
))->SpecialInfo
)->VertPot
* hidden
+ (MAXPOT
/ 2)) / MAXPOT
;
1223 if (pos
!= data
->scrollback_pos
) charmapcon_scroll_to(cl
,o
, pos
);
1225 else if (data
->activeGad
== (APTR
)&(data
->prop
->down
))
1227 if (data
->scrollback_pos
+ CHAR_YMAX(o
) < data
->scrollback_size
- 1)
1229 charmap_scroll_up(cl
,o
,1);
1230 charmapcon_refresh(cl
,o
,0);
1231 charmapcon_adj_prop(cl
,o
);
1234 else if (data
->activeGad
== (APTR
)&(data
->prop
->up
))
1236 if (data
->top_of_window
!= data
->top_of_scrollback
)
1238 charmap_scroll_down(cl
,o
,1);
1239 charmapcon_refresh(cl
,o
,0);
1240 charmapcon_adj_prop(cl
,o
);
1247 AROS_UFH3S(IPTR
, dispatch_charmapconclass
,
1248 AROS_UFHA(Class
*, cl
, A0
),
1249 AROS_UFHA(Object
*, o
, A2
),
1250 AROS_UFHA(Msg
, msg
, A1
)
1257 switch (msg
->MethodID
)
1260 retval
= (IPTR
)charmapcon_new(cl
, o
, (struct opSet
*)msg
);
1264 charmapcon_dispose(cl
, o
, msg
);
1267 case M_Console_DoCommand
:
1268 charmapcon_docommand(cl
, o
, (struct P_Console_DoCommand
*)msg
);
1271 case M_Console_ClearCell
:
1272 // FIXME: scroll down to end here if it's not there already.
1273 charmapcon_clearcell(cl
, o
, (struct P_Console_ClearCell
*)msg
);
1276 case M_Console_NewWindowSize
:
1277 charmapcon_newwindowsize(cl
, o
, (struct P_Console_NewWindowSize
*)msg
);
1280 case M_Console_HandleGadgets
:
1281 //D(bug("CharMapCon::HandleGadgets\n"));
1282 charmapcon_handlegadgets(cl
, o
, (struct P_Console_HandleGadgets
*)msg
);
1285 /* FIXME: Belongs in snimapcon - here temporary until refactored out
1287 case M_Console_Copy
:
1288 charmapcon_copy(cl
,o
,msg
);
1292 retval
= DoSuperMethodA(cl
, o
, msg
);
1301 #undef ConsoleDevice
1303 Class
*makeCharMapConClass(struct ConsoleBase
*ConsoleDevice
)
1308 cl
= MakeClass(NULL
, NULL
,STDCONCLASSPTR
, sizeof(struct charmapcondata
), 0UL);
1311 cl
->cl_Dispatcher
.h_Entry
= (APTR
)dispatch_charmapconclass
;
1312 cl
->cl_Dispatcher
.h_SubEntry
= NULL
;
1314 cl
->cl_UserData
= (IPTR
)ConsoleDevice
;