2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
5 Desc: Code for CONU_CHARMAP console units.
11 #include <exec/ports.h>
12 #include <proto/graphics.h>
13 #include <proto/intuition.h>
14 #include <proto/alib.h>
15 #include <intuition/intuition.h>
17 #include <intuition/imageclass.h>
18 #include <intuition/gadgetclass.h>
19 #include <intuition/sghooks.h>
20 #include <libraries/gadtools.h>
22 #include <graphics/rastport.h>
23 #include <aros/asmcall.h>
30 #include <aros/debug.h>
32 #include "console_gcc.h"
33 #include "consoleif.h"
38 AUTOKNOB | FREEVERT | PROPNEWLOOK | PROPBORDERLESS
41 #define CODE_PASTE 'V'
50 static const STRPTR CONCLIP_PORTNAME
= "ConClip.rendezvous";
53 struct Gadget scroller
; /* proportionnal gadget */
54 struct Gadget down
; /* down gadget */
55 struct Gadget up
; /* up gadget */
56 struct PropInfo pinfo
; /* PropInfo for scroller */
57 struct Image simage
; /* image for scroller */
58 struct Image
*upimage
; /* Boopsi image for up arrow */
59 struct Image
*downimage
; /* ditto for down arrow */
62 // FIXME: Abstract out the non-GUI aspects
65 /* Start of the scrollback buffer */
66 struct charmap_line
*top_of_scrollback
;
67 /* The line currently displayed at the top of the screen */
68 struct charmap_line
*top_of_window
;
69 /* Saved position for the top of the screen at the end of
70 the buffer; where the buffer is reset to if there is
71 output while scrolling */
72 struct charmap_line
*saved_top_of_window
;
73 ULONG saved_scrollback_pos
;
75 ULONG scrollback_size
; /* Total size of the scrollback buffer */
76 ULONG scrollback_pos
; /* Position of the top of the window */
78 ULONG scrollback_max
; /* Maximum number of lines in scrollback
79 buffer on top of CHAR_YMAX(o) */
81 BOOL unrendered
; /* Unrendered cursor while scrolled back? */
83 /* FIXME: Belongs in snipmap class */
84 /* Current selection */
87 struct charmap_line
*select_line_min
;
90 struct charmap_line
*select_line_max
;
91 BOOL active_selection
; /* If true, mouse move will affect the selection */
95 UBYTE boopsigad
; /* Type of right prop gadget of window */
102 struct Library
*ccd_GfxBase
;
106 /* Template used to quickly fill constant fields */
107 CONST
struct Scroll ScrollBar
= {
111 GFLG_RELRIGHT
| GFLG_RELHEIGHT
,
112 GACT_RIGHTBORDER
| GACT_FOLLOWMOUSE
| GACT_IMMEDIATE
|
115 NULL
, NULL
, NULL
, 0, NULL
,
121 GFLG_RELRIGHT
| GFLG_RELBOTTOM
| GFLG_GADGHIMAGE
|
123 GACT_RIGHTBORDER
| GACT_RELVERIFY
| GACT_IMMEDIATE
,
125 NULL
, NULL
, NULL
, 0, NULL
,
131 GFLG_RELRIGHT
| GFLG_RELBOTTOM
| GFLG_GADGHIMAGE
|
133 GACT_RIGHTBORDER
| GACT_RELVERIFY
| GACT_IMMEDIATE
,
135 NULL
, NULL
, NULL
, 0, NULL
,
144 /* Other values may be NULL */
149 #define ConsoleDevice ((struct ConsoleBase *)cl->cl_UserData)
152 #define GfxBase (((struct charmapcondata *)INST_DATA(cl, o))->ccd_GfxBase)
154 static VOID
charmapcon_refresh(Class
*cl
, Object
*o
, LONG off
);
156 /*** Allocate and attach a prop gadget to the window ***/
157 static VOID
charmapcon_add_prop(Class
*cl
, Object
*o
)
159 struct charmapcondata
*data
= INST_DATA(cl
, o
);
162 struct Window
*win
= CU(o
)->cu_Window
;
164 UWORD height
, size_width
, size_height
;
166 /* If the window is a backdrop'ed one, use a simplified BOOPSI propgadget
167 * because the next propgadget aspect depends on window activated state */
168 if (win
->Flags
& WFLG_BACKDROP
)
170 /* Yes this is actually a (struct Gadget *)... */
171 if ((data
->prop
= pg
=
172 (struct Scroll
*)NewObject(NULL
, "propgclass", GA_Top
, 0,
173 GA_Left
, win
->Width
- 10, GA_Width
, 10, GA_Height
,
174 win
->Height
, GA_RelVerify
, TRUE
, GA_FollowMouse
, TRUE
,
175 GA_Immediate
, TRUE
, PGA_VertPot
, MAXPOT
, PGA_VertBody
,
176 MAXBODY
, PGA_Freedom
, FREEVERT
, PGA_NewLook
, TRUE
,
179 /* And finally, add it to the window */
180 AddGList(win
, (struct Gadget
*)pg
, 0, 1, NULL
);
181 RefreshGList((struct Gadget
*)pg
, win
, NULL
, 1);
184 data
->boopsigad
= TRUE
;
187 data
->boopsigad
= FALSE
;
190 if ((data
->prop
= pg
= (void *)AllocMem(sizeof(*pg
), MEMF_PUBLIC
)))
192 /* Copy default flags/modes/etc. */
193 CopyMem(&ScrollBar
, pg
, sizeof(*pg
));
194 pg
->pinfo
.Flags
= PROP_FLAGS
;
197 di
= (void *)GetScreenDrawInfo(win
->WScreen
);
199 /* We need to get size-gadget height, to adjust properly arrows */
200 if ((dummy
= (struct Image
*)NewObject(NULL
, "sysiclass",
201 SYSIA_Which
, SIZEIMAGE
,
202 SYSIA_DrawInfo
, (IPTR
) di
, TAG_END
)))
204 size_width
= dummy
->Width
; /* width of up/down-gadgets */
205 size_height
= dummy
->Height
; /* bottom offset */
207 /* We don't need the image anymore */
208 DisposeObject(dummy
);
210 /* Get the boopsi image of the up and down arrow */
211 if ((pg
->upimage
= (struct Image
*)NewObject(NULL
, "sysiclass",
212 SYSIA_Which
, UPIMAGE
,
213 SYSIA_DrawInfo
, (IPTR
) di
, TAG_END
)))
215 pg
->up
.GadgetRender
= pg
->up
.SelectRender
=
217 height
= pg
->upimage
->Height
;
220 (struct Image
*)NewObject(NULL
, "sysiclass",
221 SYSIA_Which
, DOWNIMAGE
, SYSIA_DrawInfo
,
222 (IPTR
) di
, TAG_END
)))
224 struct Gadget
*G
= (void *)pg
;
225 WORD hoffset
= size_width
/ 4;
227 pg
->down
.GadgetRender
= pg
->down
.SelectRender
=
228 (APTR
) pg
->downimage
;
230 /* Release drawinfo */
231 FreeScreenDrawInfo(win
->WScreen
, di
);
233 /* Now init all sizes/positions relative to window's
236 -(win
->BorderTop
+ size_height
+ 2 * height
+ 2);
237 G
->TopEdge
= win
->BorderTop
+ 1;
238 G
->Width
= size_width
- hoffset
* 2 + 2;
239 G
->LeftEdge
= -(size_width
- hoffset
);
241 pg
->up
.LeftEdge
= G
->LeftEdge
= -(size_width
- 1);
242 G
->Width
= pg
->up
.Width
= size_width
;
243 G
->Height
= pg
->up
.Height
= height
;
244 G
->TopEdge
= -(size_height
+ height
- 1);
245 pg
->up
.TopEdge
= G
->TopEdge
- height
;
248 pg
->scroller
.GadgetRender
= (APTR
) &pg
->simage
;
249 pg
->scroller
.SpecialInfo
= (APTR
) &pg
->pinfo
;
252 pg
->scroller
.NextGadget
= &pg
->up
;
253 pg
->up
.NextGadget
= &pg
->down
;
255 /* And finally, add them to the window */
256 AddGList(win
, &pg
->scroller
, 0, 3, NULL
);
257 RefreshGList(&pg
->scroller
, win
, NULL
, 3);
261 DisposeObject(pg
->upimage
);
264 FreeMem(pg
, sizeof(*pg
));
265 FreeScreenDrawInfo(win
->WScreen
, di
);
270 static VOID
charmapcon_adj_prop(Class
*cl
, Object
*o
)
272 struct charmapcondata
*data
= INST_DATA(cl
, o
);
273 struct Window
*w
= CU(o
)->cu_Window
;
274 ULONG VertBody
, VertPot
;
277 data
->scrollback_size
>
278 CHAR_YMAX(o
) ? data
->scrollback_size
- CHAR_YMAX(o
) - 1 : 0;
282 VertPot
= (data
->scrollback_pos
) * MAXPOT
/ hidden
;
283 VertBody
= CHAR_YMAX(o
) * MAXBODY
/ data
->scrollback_size
;
291 if (VertPot
> MAXPOT
)
294 D(bug("VERTPOT SET TOO HIGH. Adjusted\n"));
297 NewModifyProp((struct Gadget
*)&(data
->prop
->scroller
), w
, NULL
,
298 ((struct PropInfo
*)data
->prop
->scroller
.SpecialInfo
)->Flags
,
299 MAXPOT
, VertPot
, MAXBODY
, VertBody
, 1);
302 /*** Free resources allocated for scroller ***/
303 void charmapcon_free_prop(Class
*cl
, Object
*o
)
305 struct charmapcondata
*data
= INST_DATA(cl
, o
);
306 struct Window
*win
= CU(o
)->cu_Window
;
313 RemoveGadget(win
, (struct Gadget
*)data
->prop
);
315 RemoveGList(win
, &data
->prop
->scroller
, 3);
318 DisposeObject(data
->prop
);
322 DisposeObject(data
->prop
->upimage
);
323 DisposeObject(data
->prop
->downimage
);
326 FreeMem(data
->prop
, sizeof(*data
->prop
));
333 /*********** CharMapCon::New() **********************/
335 static Object
*charmapcon_new(Class
*cl
, Object
*o
, struct opSet
*msg
)
337 EnterFunc(bug("CharMapCon::New()\n"));
338 APTR newGfxBase
= TaggedOpenLibrary(TAGGEDOPEN_GRAPHICS
);
339 if (newGfxBase
== NULL
)
342 o
= (Object
*) DoSuperMethodA(cl
, o
, (Msg
) msg
);
345 struct charmapcondata
*data
= INST_DATA(cl
, o
);
347 /* Clear for checking inside dispose() whether stuff was allocated.
348 Basically this is bug-prevention.
350 memset(data
, 0, sizeof(struct charmapcondata
));
352 data
->scrollback_max
= 500; /* FIXME: Don't hardcode it */
353 data
->ccd_GfxBase
= newGfxBase
;
354 charmapcon_add_prop(cl
, o
);
356 ReturnPtr("CharMapCon::New", Object
*, o
);
359 CloseLibrary(newGfxBase
);
360 ReturnPtr("CharMapCon::New", Object
*, NULL
);
364 /*********** CharMapCon::Dispose() **************************/
366 static VOID
charmapcon_dispose(Class
*cl
, Object
*o
, Msg msg
)
368 struct charmapcondata
*data
= INST_DATA(cl
, o
);
370 charmap_dispose_lines(data
->top_of_scrollback
);
371 charmapcon_free_prop(cl
, o
);
373 CloseLibrary(data
->ccd_GfxBase
);
375 DoSuperMethodA(cl
, o
, msg
);
378 /********* CharMapCon::DoCommand() ****************************/
380 static struct charmap_line
*charmapcon_find_line(Class
*cl
, Object
*o
,
383 struct charmapcondata
*data
= INST_DATA(cl
, o
);
385 // Find the line. This is inefficient but the number of lines on screen
386 // should never be very high.
387 // FIXME: Optimizing the case of appending to the end (e.g. know what line
388 // is on the last line of the buffer).
390 struct charmap_line
*line
= data
->top_of_window
;
393 D(bug("Initializing charmap\n"));
394 data
->top_of_window
= data
->top_of_scrollback
= line
=
395 charmap_newline(0, 0);
396 data
->scrollback_size
= 1;
399 D(bug("Finding line %ld\n", ycp
));
404 charmap_newline(0, line
);
405 data
->scrollback_size
+= 1;
411 while (data
->scrollback_size
> data
->scrollback_max
+ CHAR_YMAX(o
) &&
412 data
->top_of_window
!= data
->top_of_scrollback
)
414 data
->scrollback_size
-= 1;
415 data
->scrollback_pos
-= 1;
417 /* FIXME: Needs testing... */
418 if (data
->select_line_max
== data
->select_line_min
&&
419 data
->select_line_min
== data
->top_of_scrollback
)
421 /* The entire selection has scrolled out, but we keep it in case
424 data
->select_line_min
= data
->select_line_min
->next
;
425 data
->select_line_max
= data
->select_line_min
;
426 data
->select_x_min
= 0;
427 data
->select_x_max
= 0;
428 data
->select_y_min
+= 1;
429 data
->select_y_max
+= 1;
431 else if (data
->top_of_scrollback
== data
->select_line_min
)
433 data
->select_line_min
= data
->select_line_min
->next
;
434 data
->select_y_min
+= 1;
435 data
->select_x_min
= 0;
437 else if (data
->top_of_scrollback
== data
->select_line_max
)
439 /* "Reversed" selection */
440 data
->select_line_max
= data
->select_line_max
->next
;
441 data
->select_y_max
+= 1;
442 data
->select_x_max
= 0;
445 data
->top_of_scrollback
=
446 charmap_dispose_line(data
->top_of_scrollback
);
452 static VOID
charmap_ascii(Class
*cl
, Object
*o
, ULONG xcp
, ULONG ycp
,
453 char *str
, ULONG len
)
455 struct charmap_line
*line
= charmapcon_find_line(cl
, o
, ycp
);
456 ULONG oldsize
= line
->size
;
458 // Ensure the line has sufficient capacity.
459 if (line
->size
< xcp
+ len
)
460 charmap_resize(line
, xcp
+ len
);
462 // .. copy the required data
463 memset(line
->fgpen
+ xcp
, CU(o
)->cu_FgPen
, len
);
464 memset(line
->bgpen
+ xcp
, CU(o
)->cu_BgPen
, len
);
465 memset(line
->flags
+ xcp
, CU(o
)->cu_TxFlags
, len
);
466 memcpy(line
->text
+ xcp
, str
, len
);
468 // If cursor output is moved further right on the screen than
469 // the last output, we need to fill the line
472 memset(line
->fgpen
+ oldsize
, CU(o
)->cu_FgPen
, xcp
- oldsize
);
473 memset(line
->bgpen
+ oldsize
, CU(o
)->cu_BgPen
, xcp
- oldsize
);
474 memset(line
->flags
+ oldsize
, CU(o
)->cu_TxFlags
, xcp
- oldsize
);
475 memset(line
->text
+ oldsize
, ' ', xcp
- oldsize
);
479 static VOID
charmap_scroll_up(Class
*cl
, Object
*o
, ULONG y
)
481 struct charmapcondata
*data
= INST_DATA(cl
, o
);
483 if (!data
->top_of_window
)
488 if (!data
->top_of_window
->next
)
490 charmap_newline(0, data
->top_of_window
);
491 data
->scrollback_size
+= 1;
493 data
->top_of_window
= data
->top_of_window
->next
;
494 data
->scrollback_pos
+= 1;
495 data
->select_y_max
-= 1;
496 data
->select_y_min
-= 1;
499 if (data
->scrollback_size
- CHAR_YMAX(o
) - 1 <= data
->scrollback_pos
&&
502 Console_RenderCursor(o
);
503 data
->unrendered
= 0;
506 while (data
->scrollback_size
> data
->scrollback_max
+ CHAR_YMAX(o
) &&
507 data
->top_of_window
!= data
->top_of_scrollback
)
509 data
->scrollback_size
-= 1;
510 data
->scrollback_pos
-= 1;
511 data
->top_of_scrollback
=
512 charmap_dispose_line(data
->top_of_scrollback
);
516 static VOID
charmap_scroll_down(Class
*cl
, Object
*o
, ULONG y
)
518 // FIXME: Need to adjust cursor position or reset to bottom when editing.
519 struct charmapcondata
*data
= INST_DATA(cl
, o
);
520 if (!data
->unrendered
)
522 Console_UnRenderCursor(o
);
523 data
->unrendered
= 1;
524 data
->saved_scrollback_pos
= data
->scrollback_pos
;
525 data
->saved_top_of_window
= data
->top_of_window
;
527 // FIXME: Select position.
529 if (data
->top_of_window
)
531 while (y
-- && data
->top_of_window
->prev
)
533 data
->top_of_window
= data
->top_of_window
->prev
;
534 data
->scrollback_pos
-= 1;
535 data
->select_y_max
+= 1;
536 data
->select_y_min
+= 1;
541 static VOID
charmapcon_scroll_to(Class
*cl
, Object
*o
, ULONG y
)
543 struct charmapcondata
*data
= INST_DATA(cl
, o
);
544 struct Window
*w
= CU(o
)->cu_Window
;
545 struct RastPort
*rp
= w
->RPort
;
546 LONG off
= data
->scrollback_pos
- y
;
547 LONG old_pos
= data
->scrollback_pos
;
552 Console_UnRenderCursor(o
);
555 charmap_scroll_down(cl
, o
, off
);
557 charmap_scroll_up(cl
, o
, -off
);
559 /* Correct offset to account for the fact we might reach the
560 * top or bottom of the buffer:
562 off
= old_pos
- data
->scrollback_pos
;
564 /* A whole screenful? If so we have no choice but a full refresh
565 * (though we could double buffer... Not sure that's worth the
568 if (abs(off
) > CHAR_YMAX(o
))
570 charmapcon_refresh(cl
, o
, 0);
571 Console_RenderCursor(o
);
575 /* Avoid a full refresh by scrolling the rastport.
576 * Use "standard" background to reduce flicker
578 SetBPen(rp
, CU(o
)->cu_BgPen
);
584 GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
), GFX_YMAX(o
));
591 GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
), GFX_YMAX(o
));
595 /* Partial refresh */
596 charmapcon_refresh(cl
, o
, off
);
598 Console_RenderCursor(o
);
602 static VOID
charmap_delete_char(Class
*cl
, Object
*o
, ULONG x
, ULONG y
)
604 struct charmap_line
*line
= charmapcon_find_line(cl
, o
, y
);
606 if (!line
|| x
>= line
->size
)
609 // FIXME: Shrink the buffer, or keep track of capacity separately.
610 if (x
+ 1 >= line
->size
)
616 memmove(line
->fgpen
+ x
, line
->fgpen
+ x
+ 1, 1);
617 memmove(line
->bgpen
+ x
, line
->bgpen
+ x
+ 1, 1);
618 memmove(line
->flags
+ x
, line
->flags
+ x
+ 1, 1);
619 memmove(line
->text
+ x
, line
->text
+ x
+ 1, 1);
622 static VOID
charmap_insert_char(Class
*cl
, Object
*o
, ULONG x
, ULONG y
)
624 struct charmap_line
*line
= charmapcon_find_line(cl
, o
, y
);
629 /* FIXME: This is wasteful, since it copies the buffers straight over,
630 * so we have to do memmove's further down. */
631 charmap_resize(line
, line
->size
+ 1);
633 memmove(line
->fgpen
+ x
+ 1, line
->fgpen
+ x
, line
->size
- x
- 1);
634 memmove(line
->bgpen
+ x
+ 1, line
->bgpen
+ x
, line
->size
- x
- 1);
635 memmove(line
->flags
+ x
+ 1, line
->flags
+ x
, line
->size
- x
- 1);
636 memmove(line
->text
+ x
+ 1, line
->text
+ x
, line
->size
- x
- 1);
638 line
->fgpen
[x
] = CU(o
)->cu_FgPen
;
639 line
->bgpen
[x
] = CU(o
)->cu_BgPen
;
640 line
->flags
[x
] = CU(o
)->cu_TxFlags
;
644 static VOID
charmap_formfeed(Class
*cl
, Object
*o
)
646 struct charmapcondata
*data
= INST_DATA(cl
, o
);
647 struct charmap_line
*line
= data
->top_of_window
;
651 charmap_resize(line
, 0);
656 static VOID
charmapcon_docommand(Class
*cl
, Object
*o
,
657 struct P_Console_DoCommand
*msg
)
659 IPTR
*params
= msg
->Params
;
660 struct charmapcondata
*data
= INST_DATA(cl
, o
);
663 ("CharMapCon::DoCommand(o=%p, cmd=%d, params=%p) x=%d, y=%d, ymax=%d\n",
664 o
, msg
->Command
, params
, XCP
, YCP
, CHAR_YMAX(o
)));
666 // This is a bit of a hack: Set position to bottom in order to prevent
667 // output while scrolled.
669 ULONG old_scrollback_size
= data
->scrollback_size
;
670 ULONG old_scrollback_pos
= data
->scrollback_pos
;
672 if (data
->unrendered
)
674 data
->unrendered
= 0;
675 data
->scrollback_pos
= data
->saved_scrollback_pos
;
676 data
->select_y_min
+= old_scrollback_pos
- data
->scrollback_pos
;
677 data
->select_y_max
+= old_scrollback_pos
- data
->scrollback_pos
;
678 data
->top_of_window
= data
->saved_top_of_window
;
679 charmapcon_refresh(cl
, o
, 0);
680 Console_RenderCursor(o
);
683 switch (msg
->Command
)
686 charmap_ascii(cl
, o
, XCP
, YCP
, (char *)¶ms
[0], 1);
687 DoSuperMethodA(cl
, o
, (Msg
) msg
);
691 charmap_ascii(cl
, o
, XCP
, YCP
, (char *)params
[0], (int)params
[1]);
692 DoSuperMethodA(cl
, o
, (Msg
) msg
);
696 charmap_formfeed(cl
, o
);
697 DoSuperMethodA(cl
, o
, (Msg
) msg
);
700 case C_DELETE_CHAR
: /* FIXME: can it have params!? */
701 charmap_delete_char(cl
, o
, XCP
, YCP
);
702 DoSuperMethodA(cl
, o
, (Msg
) msg
);
706 charmap_insert_char(cl
, o
, XCP
, YCP
);
707 DoSuperMethodA(cl
, o
, (Msg
) msg
);
712 D(bug("C_SCROLL_UP area (%d, %d) to (%d, %d), %d\n",
713 GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
), GFX_YMAX(o
),
714 YRSIZE
* params
[0]));
715 charmap_scroll_up(cl
, o
, params
[0]);
716 DoSuperMethodA(cl
, o
, (Msg
) msg
);
722 D(bug("C_SCROLL_DOWN area (%d, %d) to (%d, %d), %d\n",
723 GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
), GFX_YMAX(o
),
724 YRSIZE
* params
[0]));
725 charmap_scroll_down(cl
, o
, params
[0]);
726 DoSuperMethodA(cl
, o
, (Msg
) msg
);
731 DoSuperMethodA(cl
, o
, (Msg
) msg
);
735 if (old_scrollback_size
!= data
->scrollback_size
||
736 old_scrollback_pos
!= data
->scrollback_pos
)
737 charmapcon_adj_prop(cl
, o
);
739 ReturnVoid("CharMapCon::DoCommand");
742 /**************************
743 ** CharMapCon::ClearCell() **
744 **************************/
745 static VOID
charmapcon_clearcell(Class
*cl
, Object
*o
,
746 struct P_Console_ClearCell
*msg
)
748 // FIXME, insert space.
749 DoSuperMethodA(cl
, o
, (Msg
) msg
);
752 static VOID
charmapcon_refresh_lines(Class
*cl
, Object
*o
, LONG fromLine
,
755 struct Window
*w
= CU(o
)->cu_Window
;
756 struct RastPort
*rp
= w
->RPort
;
757 struct charmapcondata
*data
= INST_DATA(cl
, o
);
759 if (fromLine
< CHAR_YMIN(o
))
760 fromLine
= CHAR_YMIN(o
);
761 if (toLine
> CHAR_YMAX(o
))
762 toLine
= CHAR_YMAX(o
);
764 D(bug("fromLine: %ld, toLine: %ld, char_ymax: %ld\n", fromLine
, toLine
,
767 if (toLine
< fromLine
)
770 Console_UnRenderCursor(o
);
772 D(bug("Rendering charmap\n"));
774 struct charmap_line
*line
= charmapcon_find_line(cl
, o
, fromLine
);
775 ULONG y
= GFX_YMIN(o
) + fromLine
* YRSIZE
+ rp
->Font
->tf_Baseline
;
779 LONG selectstart_x
, selectstart_y
, selectend_x
, selectend_y
;
780 if (data
->select_y_min
== data
->select_y_max
)
782 selectstart_y
= selectend_y
= data
->select_y_min
;
783 selectstart_x
= MIN(data
->select_x_min
, data
->select_x_max
);
784 selectend_x
= MAX(data
->select_x_min
, data
->select_x_max
);
786 else if (data
->select_y_min
< data
->select_y_max
)
788 selectstart_y
= data
->select_y_min
;
789 selectstart_x
= data
->select_x_min
;
790 selectend_y
= data
->select_y_max
;
791 selectend_x
= data
->select_x_max
;
795 selectstart_y
= data
->select_y_max
;
796 selectstart_x
= data
->select_x_max
;
797 selectend_y
= data
->select_y_min
;
798 selectend_x
= data
->select_x_min
;
801 while (line
&& yc
<= toLine
)
803 const char *str
= line
->text
;
805 ULONG remaining_space
= CHAR_XMAX(o
) + 1;
806 Move(rp
, GFX_XMIN(o
), y
);
807 while (line
->size
> start
&& remaining_space
> 0 && str
[start
])
809 /* Identify a batch of characters with the same fgpen/bgpen
810 to avoid having to move/set pens and do Text() on single
813 UBYTE fgpen
= line
->fgpen
[start
];
814 UBYTE bgpen
= line
->bgpen
[start
];
816 /* Is any part of this line part of a selection?
817 * If so, we bake in a state transition on "stop".
818 * This code is messy - there must be a nicer way.
820 ULONG stop
= 9999999;
821 BOOL in_selection
= 0;
823 if (yc
> selectstart_y
&& yc
< selectend_y
)
827 else if (yc
== selectstart_y
)
829 if (yc
== selectend_y
)
831 if (start
>= selectstart_x
&& start
< selectend_x
)
836 else if (start
< selectstart_x
)
838 stop
= selectstart_x
;
843 if (start
>= selectstart_x
)
846 stop
= selectstart_x
;
849 else if (yc
== selectend_y
)
851 /* In this case, the selection *ends* on selectend_x */
852 if (start
< selectend_x
)
864 fgpen
= line
->bgpen
[start
];
865 bgpen
= line
->fgpen
[start
];
869 while (line
->size
> start
+ len
&& str
[start
+ len
] &&
870 len
< remaining_space
&&
871 line
->fgpen
[start
] == line
->fgpen
[start
+ len
] &&
872 line
->bgpen
[start
] == line
->bgpen
[start
+ len
] &&
873 line
->flags
[start
] == line
->flags
[start
+ len
] &&
877 setabpen(GfxBase
, rp
, line
->flags
[start
], fgpen
, bgpen
);
878 if ((line
->flags
[start
] & CON_TXTFLAGS_MASK
) !=
879 (flags
& CON_TXTFLAGS_MASK
))
881 SetSoftStyle(rp
, line
->flags
[start
], CON_TXTFLAGS_MASK
);
883 flags
= line
->flags
[start
];
885 Text(rp
, &str
[start
], len
);
888 remaining_space
-= len
;
891 /* Clear to EOL, without overwriting scroll bar (ClearEOL does) */
892 SetAPen(rp
, CU(o
)->cu_BgPen
);
894 GFX_X(o
, start
), GFX_Y(o
, yc
),
895 GFX_XMAX(o
), GFX_Y(o
, yc
+ 1) - 1);
899 /* We want to make sure we have lines covering the window once
900 there's something to scroll back, as that simplies resize handling
903 if (!line
->next
&& yc
<= toLine
)
905 line
->next
= charmap_newline(0, line
);
906 data
->scrollback_size
+= 1;
913 SetAPen(rp
, CU(o
)->cu_BgPen
);
915 GFX_XMIN(o
), GFX_Y(o
, yc
), GFX_XMAX(o
), GFX_Y(o
, toLine
));
918 Console_RenderCursor(o
);
923 * Refresh the full console unless "off" is provided.
924 * If off is set to a positive value, it indicates the
925 * number of rows from the top we start rendering.
926 * If off is set to a negative value, it indicates the
927 * number of rows from the top we stop rendering.
928 * This is used for partial refreshes when the screen is
931 static VOID
charmapcon_refresh(Class
*cl
, Object
*o
, LONG off
)
934 LONG toLine
= CHAR_YMAX(o
) - CHAR_YMIN(o
);
939 fromLine
= CHAR_YMAX(o
) + off
+ 1;
941 charmapcon_refresh_lines(cl
, o
, fromLine
, toLine
);
945 static ULONG
charmapcon_calc_selection_size(struct charmap_line
*first
,
946 struct charmap_line
*last
, ULONG minx
, ULONG maxx
)
948 struct charmap_line
*cur
= first
;
957 size
+= abs(maxx
- minx
);
959 size
+= cur
->size
- minx
;
961 else if (cur
== last
)
970 if (cur
!= last
|| (cur
== last
&& maxx
== cur
->size
))
971 size
+= 1; /* line feed */
980 char *charmapcon_get_selection(ULONG size
,
981 struct charmap_line
*first
,
982 struct charmap_line
*last
, ULONG minx
, ULONG maxx
)
984 char *buf
= AllocMem(size
, MEMF_ANY
);
986 struct charmap_line
*cur
= first
;
995 /* empty lines may not have text ptrs */
1000 CopyMem(cur
->text
+ MIN(minx
, maxx
), bufpos
,
1002 bufpos
+= abs(maxx
- minx
);
1006 CopyMem(cur
->text
+ minx
, bufpos
, cur
->size
- minx
);
1007 bufpos
+= cur
->size
- minx
;
1010 else if (cur
== last
)
1012 CopyMem(cur
->text
, bufpos
, maxx
);
1017 CopyMem(cur
->text
, bufpos
, cur
->size
);
1018 bufpos
+= cur
->size
;
1021 if (cur
!= last
|| (cur
== last
&& maxx
== cur
->size
)) /* line feed */
1035 ULONG flags
; /* always zero? */
1036 ULONG size
; /* does not include NUL termination */
1037 APTR buffer
; /* NUL-terminated string! */
1040 /* FIXME: Belongs in snipmapcon - here temporarily until refactored out
1042 static VOID
charmapcon_copy(Class
*cl
, Object
*o
, Msg copymsg
)
1044 struct charmapcondata
*data
= INST_DATA(cl
, o
);
1045 struct MsgPort replyport
, *port
;
1047 struct MyEditHookMsg msg
;
1050 struct charmap_line
*first
, *last
;
1051 ULONG minx
, maxx
, size
;
1053 /* Create a string from the contents of the scrollback buffer */
1054 if (data
->select_y_min
< data
->select_y_max
)
1056 first
= data
->select_line_min
;
1057 last
= data
->select_line_max
;
1058 minx
= data
->select_x_min
;
1059 maxx
= data
->select_x_max
;
1063 first
= data
->select_line_max
;
1064 last
= data
->select_line_min
;
1065 minx
= data
->select_x_max
;
1066 maxx
= data
->select_x_min
;
1069 size
= charmapcon_calc_selection_size(first
, last
, minx
, maxx
);
1070 buf
= charmapcon_get_selection(size
, first
, last
, minx
, maxx
);
1071 D(bug("%d bytes copied\n"));
1073 /* If Conclip is running, we prefer using that */
1074 if (IsListEmpty(&ConsoleDevice
->sniphooks
)
1075 && (port
= FindPort(CONCLIP_PORTNAME
)))
1077 /* AROS conclip format */
1078 D(bug("AROS conclip\n"));
1079 replyport
.mp_Node
.ln_Type
= NT_MSGPORT
;
1080 replyport
.mp_Node
.ln_Name
= NULL
;
1081 replyport
.mp_Node
.ln_Pri
= 0;
1082 replyport
.mp_Flags
= PA_SIGNAL
;
1083 replyport
.mp_SigBit
= SIGB_SINGLE
;
1084 replyport
.mp_SigTask
= FindTask(NULL
);
1085 NewList(&replyport
.mp_MsgList
);
1087 msg
.msg
.mn_Node
.ln_Type
= NT_MESSAGE
;
1088 msg
.msg
.mn_ReplyPort
= &replyport
;
1089 msg
.msg
.mn_Length
= sizeof(msg
);
1091 msg
.code
= CODE_COPY
;
1095 sgw
.WorkBuffer
= buf
;
1098 sgw
.Code
= CODE_COPY
;
1102 sgw
.EditOp
= EO_BIGCHANGE
;
1104 sgw
.NumChars
= size
;
1106 SetSignal(0, SIGF_SINGLE
);
1107 PutMsg(port
, &msg
.msg
);
1108 WaitPort(&replyport
);
1114 ObtainSemaphore(&ConsoleDevice
->copyBufferLock
);
1115 FreeMem((APTR
) ConsoleDevice
->copyBuffer
,
1116 ConsoleDevice
->copyBufferSize
);
1118 ConsoleDevice
->copyBuffer
= buf
;
1119 if (ConsoleDevice
->copyBuffer
)
1120 ConsoleDevice
->copyBufferSize
= size
;
1122 ConsoleDevice
->copyBufferSize
= 0;
1124 if (!IsListEmpty(&ConsoleDevice
->sniphooks
))
1126 /* OS2-3.x compatible conclip format */
1127 struct Hook
*conhook
;
1128 struct ConClipData ccd
;
1130 D(bug("AOS conclip\n"));
1131 if (ConsoleDevice
->copyBufferSize
)
1134 ccd
.size
= ConsoleDevice
->copyBufferSize
;
1135 /* must be NUL-terminated */
1136 ccd
.buffer
= AllocVec(ccd
.size
+ 1, MEMF_CLEAR
);
1139 CopyMem(ConsoleDevice
->copyBuffer
, ccd
.buffer
, ccd
.size
);
1140 ForeachNode(&ConsoleDevice
->sniphooks
, conhook
)
1142 D(bug("Calling AOS conclip hook %p\n", conhook
));
1143 CALLHOOKPKT(conhook
, NULL
, &ccd
);
1145 FreeVec(ccd
.buffer
);
1149 ReleaseSemaphore(&ConsoleDevice
->copyBufferLock
);
1153 /**********************************
1154 ** CharMapCon::NewWindowSize() **
1155 **********************************/
1156 static VOID
charmapcon_newwindowsize(Class
*cl
, Object
*o
,
1157 struct P_Console_NewWindowSize
*msg
)
1159 struct charmapcondata
*data
= INST_DATA(cl
, o
);
1163 DoSuperMethodA(cl
, o
, (Msg
) msg
);
1164 D(bug("CharMapCon::NewWindowSize(o=%p) x=%d, y=%d, ymax=%d\n",
1165 o
, XCP
, YCP
, CHAR_YMAX(o
)));
1167 // Is console empty? Unlikely, but anyway.
1168 if (!data
->top_of_window
)
1171 // Scroll up if new window size has forced the cursor up
1172 if (old_ycp
> CHAR_YMAX(o
))
1174 charmap_scroll_up(cl
, o
, old_ycp
- CHAR_YMAX(o
));
1177 charmapcon_refresh(cl
, o
, 0);
1180 static VOID
charmapcon_handlemouse(Class
*cl
, Object
*o
,
1181 struct P_Console_HandleGadgets
*msg
)
1183 struct charmapcondata
*data
= INST_DATA(cl
, o
);
1184 struct Window
*w
= CU(o
)->cu_Window
;
1185 struct InputEvent
*e
= msg
->Event
;
1187 /* We have the following states:
1188 * - No active selection and no mouse button => ignore
1189 * - No active selection and left mouse button => Start selection
1190 * - Active selection and no mouse button => End selection
1191 * - Active selection and left mouse button => Update selection
1197 * Take window-relative mouse position.
1198 * The original code here failed when the screen was moved away from (0, 0)
1199 * position. x and y were unaware of shifted display (ie_X and ie_Y are
1200 * raw physical coordinates, and w->LeftEdge and w->TopEdge are screen-
1201 * relative), and this caused lockups here.
1202 * TODO: Verify this intuition's behavior with AmigaOS3.1 using screentest
1205 x = e->ie_X - w->LeftEdge;
1206 y = e->ie_Y - w->TopEdge;
1211 /* Active selection */
1213 if (!(e
->ie_Qualifier
& IEQUALIFIER_LEFTBUTTON
))
1216 data
->active_selection
= 0;
1217 data
->ignore_drag
= 0;
1221 if (!data
->active_selection
)
1223 if (e
->ie_Qualifier
& IEQUALIFIER_LEFTBUTTON
&& !data
->ignore_drag
)
1225 /* Inside the console area? */
1226 if (x
>= GFX_XMIN(o
) && x
<= GFX_XMAX(o
) &&
1227 y
>= GFX_YMIN(o
) && y
<= GFX_YMAX(o
))
1229 D(bug("activated selection with x: %ld, y: %ld,"
1230 " xmin: %ld, ymin: %Ld, xmax: %ld, ymax: %ld\n",
1231 x
, y
, GFX_XMIN(o
), GFX_YMIN(o
), GFX_XMAX(o
),
1234 /* We need to clear these lines
1237 MIN(data
->select_y_min
, data
->select_y_max
);
1239 MAX(data
->select_y_min
, data
->select_y_max
);
1241 /* Yes, so start selection */
1242 data
->active_selection
= 1;
1243 data
->ignore_drag
= 0;
1244 data
->select_x_min
= (x
- GFX_XMIN(o
)) / XRSIZE
;
1245 data
->select_y_min
= (y
- GFX_YMIN(o
)) / YRSIZE
;
1246 data
->select_line_min
=
1247 charmapcon_find_line(cl
, o
, data
->select_y_min
);
1249 data
->select_x_max
= data
->select_x_min
;
1250 data
->select_y_max
= data
->select_y_min
;
1254 /* FIXME: Determine exactly which lines are affected, as
1257 - If max_y has increased, render from old_max_y to new
1258 - If min_y has decreased, refresh from new min y to old
1259 - If both have changed, refresh in two batches.
1260 - If there's been a reversal of the relative positions of
1261 y_min/y_max, refresh the entire range.
1263 charmapcon_refresh_lines(cl
, o
, old_min_y
, old_max_y
);
1267 data
->ignore_drag
= 1;
1272 /* Update selection. */
1273 if (x
>= GFX_XMIN(o
) && x
<= GFX_XMAX(o
) &&
1274 y
>= GFX_YMIN(o
) && y
<= GFX_YMAX(o
))
1278 xmax
= data
->select_x_max
;
1279 ymax
= data
->select_y_max
;
1280 data
->select_x_max
= (x
- GFX_XMIN(o
)) / XRSIZE
;
1281 data
->select_y_max
= (y
- GFX_YMIN(o
)) / YRSIZE
;
1282 data
->select_line_max
=
1283 charmapcon_find_line(cl
, o
, data
->select_y_max
);
1284 /* FIXME: More intelligent refresh */
1285 if (xmax
!= data
->select_x_max
|| ymax
!= data
->select_y_max
)
1287 charmapcon_refresh_lines(cl
, o
, MIN(ymax
,
1288 MIN(data
->select_y_min
, data
->select_y_max
)), MAX(ymax
,
1289 MAX(data
->select_y_min
, data
->select_y_max
)));
1294 /* FIXME: Outside the console area, we need to scroll the window
1295 and just update the selection based on that */
1300 static VOID
charmapcon_handlegadgets(Class
*cl
, Object
*o
,
1301 struct P_Console_HandleGadgets
*msg
)
1303 struct InputEvent
*e
= msg
->Event
;
1305 if (e
->ie_Class
== IECLASS_RAWMOUSE
)
1307 charmapcon_handlemouse(cl
, o
, msg
);
1311 struct charmapcondata
*data
= INST_DATA(cl
, o
);
1313 if (e
->ie_Class
== IECLASS_GADGETUP
)
1315 data
->activeGad
= 0;
1319 if (e
->ie_Class
== IECLASS_GADGETDOWN
)
1321 /* We pass 0 from consoletask if the mouse wheel is being used */
1322 if ((IPTR
) e
->ie_EventAddress
== 1)
1323 data
->activeGad
= (APTR
) &(data
->prop
->up
);
1324 else if ((IPTR
) e
->ie_EventAddress
== 2)
1325 data
->activeGad
= (APTR
) &(data
->prop
->down
);
1327 data
->activeGad
= e
->ie_EventAddress
;
1330 if (data
->activeGad
== (APTR
) &(data
->prop
->scroller
))
1333 data
->scrollback_size
>
1334 CHAR_YMAX(o
) ? data
->scrollback_size
- CHAR_YMAX(o
) - 1 : 0;
1336 (((struct PropInfo
*)((struct Gadget
*)&(data
->prop
->
1337 scroller
))->SpecialInfo
)->VertPot
* hidden
+
1338 (MAXPOT
/ 2)) / MAXPOT
;
1340 if (pos
!= data
->scrollback_pos
)
1341 charmapcon_scroll_to(cl
, o
, pos
);
1343 else if (data
->activeGad
== (APTR
) &(data
->prop
->down
))
1345 if (data
->scrollback_pos
+ CHAR_YMAX(o
) < data
->scrollback_size
- 1)
1347 charmap_scroll_up(cl
, o
, 1);
1348 charmapcon_refresh(cl
, o
, 0);
1349 charmapcon_adj_prop(cl
, o
);
1352 else if (data
->activeGad
== (APTR
) &(data
->prop
->up
))
1354 if (data
->top_of_window
!= data
->top_of_scrollback
)
1356 charmap_scroll_down(cl
, o
, 1);
1357 charmapcon_refresh(cl
, o
, 0);
1358 charmapcon_adj_prop(cl
, o
);
1365 AROS_UFH3S(IPTR
, dispatch_charmapconclass
,
1366 AROS_UFHA(Class
*, cl
, A0
),
1367 AROS_UFHA(Object
*, o
, A2
), AROS_UFHA(Msg
, msg
, A1
))
1373 switch (msg
->MethodID
)
1376 retval
= (IPTR
) charmapcon_new(cl
, o
, (struct opSet
*)msg
);
1380 charmapcon_dispose(cl
, o
, msg
);
1383 case M_Console_DoCommand
:
1384 charmapcon_docommand(cl
, o
, (struct P_Console_DoCommand
*)msg
);
1387 case M_Console_ClearCell
:
1388 // FIXME: scroll down to end here if it's not there already.
1389 charmapcon_clearcell(cl
, o
, (struct P_Console_ClearCell
*)msg
);
1392 case M_Console_NewWindowSize
:
1393 charmapcon_newwindowsize(cl
, o
,
1394 (struct P_Console_NewWindowSize
*)msg
);
1397 case M_Console_HandleGadgets
:
1398 //D(bug("CharMapCon::HandleGadgets\n"));
1399 charmapcon_handlegadgets(cl
, o
,
1400 (struct P_Console_HandleGadgets
*)msg
);
1403 /* FIXME: Belongs in snimapcon - here temporarily until refactored out
1405 case M_Console_Copy
:
1406 charmapcon_copy(cl
, o
, msg
);
1410 retval
= DoSuperMethodA(cl
, o
, msg
);
1419 #undef ConsoleDevice
1421 Class
*makeCharMapConClass(struct ConsoleBase
*ConsoleDevice
)
1426 cl
= MakeClass(NULL
, NULL
, STDCONCLASSPTR
,
1427 sizeof(struct charmapcondata
), 0UL);
1430 cl
->cl_Dispatcher
.h_Entry
= (APTR
) dispatch_charmapconclass
;
1431 cl
->cl_Dispatcher
.h_SubEntry
= NULL
;
1433 cl
->cl_UserData
= (IPTR
) ConsoleDevice
;