source: mainline/uspace/srv/fb/fb.c@ 67c6c651

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 67c6c651 was 67c6c651, checked in by Jiri Svoboda <jirik.svoboda@…>, 17 years ago

Re-instate accidentaly lost fb scrolling implementation. Also re-apply accidentaly reverted fix of unsing memmove() instead of memcpy().

  • Property mode set to 100644
File size: 30.7 KB
Line 
1/*
2 * Copyright (c) 2008 Martin Decky
3 * Copyright (c) 2006 Jakub Vana
4 * Copyright (c) 2006 Ondrej Palkovsky
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @defgroup fb Graphical framebuffer
33 * @brief HelenOS graphical framebuffer.
34 * @ingroup fbs
35 * @{
36 */
37
38/** @file
39 */
40
41#include <stdlib.h>
42#include <unistd.h>
43#include <string.h>
44#include <ddi.h>
45#include <sysinfo.h>
46#include <align.h>
47#include <as.h>
48#include <ipc/fb.h>
49#include <ipc/ipc.h>
50#include <ipc/ns.h>
51#include <ipc/services.h>
52#include <kernel/errno.h>
53#include <kernel/genarch/fb/visuals.h>
54#include <async.h>
55#include <bool.h>
56
57#include "font-8x16.h"
58#include "fb.h"
59#include "main.h"
60#include "../console/screenbuffer.h"
61#include "ppm.h"
62
63#include "pointer.xbm"
64#include "pointer_mask.xbm"
65
66#define DEFAULT_BGCOLOR 0xf0f0f0
67#define DEFAULT_FGCOLOR 0x000000
68
69#define MAX_ANIM_LEN 8
70#define MAX_ANIMATIONS 4
71#define MAX_PIXMAPS 256 /**< Maximum number of saved pixmaps */
72#define MAX_VIEWPORTS 128 /**< Viewport is a rectangular area on the screen */
73
74typedef void (*rgb_conv_t)(void *, uint32_t);
75
76struct {
77 uint8_t *fb_addr;
78
79 unsigned int xres;
80 unsigned int yres;
81
82 unsigned int scanline;
83 unsigned int glyphscanline;
84
85 unsigned int pixelbytes;
86 unsigned int glyphbytes;
87
88 rgb_conv_t rgb_conv;
89} screen;
90
91typedef struct {
92 bool initialized;
93 unsigned int x;
94 unsigned int y;
95 unsigned int width;
96 unsigned int height;
97
98 /* Text support in window */
99 unsigned int cols;
100 unsigned int rows;
101
102 /* Style and glyphs for text printing */
103 style_t style;
104 uint8_t *glyphs;
105 uint8_t *bgpixel;
106
107 /* Auto-cursor position */
108 bool cursor_active;
109 unsigned int cur_col;
110 unsigned int cur_row;
111 bool cursor_shown;
112
113 /* Back buffer */
114 unsigned int bbsize;
115 uint8_t *backbuf;
116} viewport_t;
117
118typedef struct {
119 bool initialized;
120 bool enabled;
121 unsigned int vp;
122
123 unsigned int pos;
124 unsigned int animlen;
125 unsigned int pixmaps[MAX_ANIM_LEN];
126} animation_t;
127
128static animation_t animations[MAX_ANIMATIONS];
129static bool anims_enabled;
130
131typedef struct {
132 unsigned int width;
133 unsigned int height;
134 uint8_t *data;
135} pixmap_t;
136
137static pixmap_t pixmaps[MAX_PIXMAPS];
138static viewport_t viewports[128];
139
140static bool client_connected = false; /**< Allow only 1 connection */
141
142static void draw_glyph(unsigned int x, unsigned int y, bool cursor,
143 uint8_t *glyphs, uint8_t glyph);
144static void draw_vp_glyph(viewport_t *vport, bool cursor, unsigned int col,
145 unsigned int row);
146
147
148#define RED(x, bits) ((x >> (8 + 8 + 8 - bits)) & ((1 << bits) - 1))
149#define GREEN(x, bits) ((x >> (8 + 8 - bits)) & ((1 << bits) - 1))
150#define BLUE(x, bits) ((x >> (8 - bits)) & ((1 << bits) - 1))
151
152#define COL2X(col) ((col) * FONT_WIDTH)
153#define ROW2Y(row) ((row) * FONT_SCANLINES)
154
155#define X2COL(x) ((x) / FONT_WIDTH)
156#define Y2ROW(y) ((y) / FONT_SCANLINES)
157
158#define FB_POS(x, y) ((y) * screen.scanline + (x) * screen.pixelbytes)
159#define BB_POS(vport, col, row) ((row) * vport->cols + (col))
160#define GLYPH_POS(glyph, y, cursor) (((glyph) + (cursor) * FONT_GLYPHS) * screen.glyphbytes + (y) * screen.glyphscanline)
161
162
163/** ARGB 8:8:8:8 conversion
164 *
165 */
166static void rgb_0888(void *dst, uint32_t rgb)
167{
168 *((uint32_t *) dst) = rgb & 0xffffff;
169}
170
171
172/** ABGR 8:8:8:8 conversion
173 *
174 */
175static void bgr_0888(void *dst, uint32_t rgb)
176{
177 *((uint32_t *) dst)
178 = (BLUE(rgb, 8) << 16) | (GREEN(rgb, 8) << 8) | RED(rgb, 8);
179}
180
181
182/** BGR 8:8:8 conversion
183 *
184 */
185static void rgb_888(void *dst, uint32_t rgb)
186{
187#if defined(FB_INVERT_ENDIAN)
188 ((uint8_t *) dst)[0] = RED(rgb, 8);
189 ((uint8_t *) dst)[1] = GREEN(rgb, 8);
190 ((uint8_t *) dst)[2] = BLUE(rgb, 8);
191#else
192 ((uint8_t *) dst)[0] = BLUE(rgb, 8);
193 ((uint8_t *) dst)[1] = GREEN(rgb, 8);
194 ((uint8_t *) dst)[2] = RED(rgb, 8);
195#endif
196}
197
198
199/** RGB 5:5:5 conversion
200 *
201 */
202static void rgb_555(void *dst, uint32_t rgb)
203{
204 *((uint16_t *) dst)
205 = (RED(rgb, 5) << 10) | (GREEN(rgb, 5) << 5) | BLUE(rgb, 5);
206}
207
208
209/** RGB 5:6:5 conversion
210 *
211 */
212static void rgb_565(void *dst, uint32_t rgb)
213{
214 *((uint16_t *) dst)
215 = (RED(rgb, 5) << 11) | (GREEN(rgb, 6) << 5) | BLUE(rgb, 5);
216}
217
218
219/** RGB 3:2:3
220 *
221 */
222static void rgb_323(void *dst, uint32_t rgb)
223{
224 *((uint8_t *) dst)
225 = ~((RED(rgb, 3) << 5) | (GREEN(rgb, 2) << 3) | BLUE(rgb, 3));
226}
227
228/** Draw a filled rectangle.
229 *
230 * @note Need real implementation that does not access VRAM twice.
231 */
232static void draw_filled_rect(unsigned int x0, unsigned int y0, unsigned int x1,
233 unsigned int y1, uint32_t color)
234{
235 unsigned int x, y;
236 unsigned int copy_bytes;
237
238 uint8_t *sp, *dp;
239 uint8_t cbuf[4];
240
241 if (y0 >= y1 || x0 >= x1) return;
242 screen.rgb_conv(cbuf, color);
243
244 sp = &screen.fb_addr[FB_POS(x0, y0)];
245 dp = sp;
246
247 /* Draw the first line. */
248 for (x = x0; x < x1; x++) {
249 memcpy(dp, cbuf, screen.pixelbytes);
250 dp += screen.pixelbytes;
251 }
252
253 dp = sp + screen.scanline;
254 copy_bytes = (x1 - x0) * screen.pixelbytes;
255
256 /* Draw the remaining lines by copying. */
257 for (y = y0 + 1; y < y1; y++) {
258 memcpy(dp, sp, copy_bytes);
259 dp += screen.scanline;
260 }
261}
262
263/** Redraw viewport.
264 *
265 * @param vport Viewport to redraw
266 *
267 */
268static void vport_redraw(viewport_t *vport)
269{
270 unsigned int row, col;
271
272 for (row = 0; row < vport->rows; row++) {
273 for (col = 0; col < vport->cols; col++) {
274 draw_vp_glyph(vport, false, col, row);
275 }
276 }
277
278 if (COL2X(vport->cols) < vport->width) {
279 draw_filled_rect(
280 vport->x + COL2X(vport->cols), vport->y,
281 vport->x + vport->width, vport->y + vport->height,
282 vport->style.bg_color);
283 }
284
285 if (ROW2Y(vport->rows) < vport->height) {
286 draw_filled_rect(
287 vport->x, vport->y + ROW2Y(vport->rows),
288 vport->x + vport->width, vport->y + vport->height,
289 vport->style.bg_color);
290 }
291}
292
293
294/** Clear viewport.
295 *
296 * @param vport Viewport to clear
297 *
298 */
299static void vport_clear(viewport_t *vport)
300{
301 memset(vport->backbuf, 0, vport->bbsize);
302 vport_redraw(vport);
303}
304
305/** Scroll viewport by the specified number of lines.
306 *
307 * @param vport Viewport to scroll
308 * @param lines Number of lines to scroll
309 *
310 */
311static void vport_scroll(viewport_t *vport, int lines)
312{
313 unsigned int row, col;
314 unsigned int x, y;
315 uint8_t glyph;
316
317 /*
318 * Redraw.
319 */
320
321 y = vport->y;
322 for (row = 0; row < vport->rows; row++) {
323 x = vport->x;
324 for (col = 0; col < vport->cols; col++) {
325 if ((row + lines >= 0) && (row + lines < vport->rows)) {
326 glyph = vport->backbuf[BB_POS(vport, col, row + lines)];
327
328 if (vport->backbuf[BB_POS(vport, col, row)] == glyph) {
329 x += FONT_WIDTH;
330 continue;
331 }
332 } else {
333 glyph = 0;
334 }
335
336 draw_glyph(x, y, false, vport->glyphs, glyph);
337 x += FONT_WIDTH;
338 }
339 y += FONT_SCANLINES;
340 }
341
342 /*
343 * Scroll backbuffer.
344 */
345
346 if (lines > 0) {
347 memmove(vport->backbuf, vport->backbuf + vport->cols * lines,
348 vport->cols * (vport->rows - lines));
349 memset(&vport->backbuf[BB_POS(vport, 0, vport->rows - lines)],
350 0, vport->cols * lines);
351 } else {
352 memmove(vport->backbuf - vport->cols * lines, vport->backbuf,
353 vport->cols * (vport->rows + lines));
354 memset(vport->backbuf, 0, - vport->cols * lines);
355 }
356}
357
358/** Render glyphs
359 *
360 * Convert glyphs from device independent font
361 * description to current visual representation.
362 *
363 * @param vport Viewport
364 *
365 */
366static void render_glyphs(viewport_t* vport)
367{
368 unsigned int glyph;
369
370 for (glyph = 0; glyph < FONT_GLYPHS; glyph++) {
371 unsigned int y;
372
373 for (y = 0; y < FONT_SCANLINES; y++) {
374 unsigned int x;
375
376 for (x = 0; x < FONT_WIDTH; x++) {
377 screen.rgb_conv(&vport->glyphs[GLYPH_POS(glyph, y, false) + x * screen.pixelbytes],
378 (fb_font[glyph * FONT_SCANLINES + y] & (1 << (7 - x)))
379 ? vport->style.fg_color : vport->style.bg_color);
380
381 uint32_t curcolor;
382
383 if (y < FONT_SCANLINES - 2)
384 curcolor =
385 (fb_font[glyph * FONT_SCANLINES + y] & (1 << (7 - x)))
386 ? vport->style.fg_color : vport->style.bg_color;
387 else
388 curcolor = vport->style.fg_color;
389
390 screen.rgb_conv(&vport->glyphs[GLYPH_POS(glyph, y, true) + x * screen.pixelbytes], curcolor);
391 }
392 }
393 }
394
395 screen.rgb_conv(vport->bgpixel, vport->style.bg_color);
396}
397
398
399/** Create new viewport
400 *
401 * @param x Origin of the viewport (x).
402 * @param y Origin of the viewport (y).
403 * @param width Width of the viewport.
404 * @param height Height of the viewport.
405 *
406 * @return New viewport number.
407 *
408 */
409static int vport_create(unsigned int x, unsigned int y,
410 unsigned int width, unsigned int height)
411{
412 unsigned int i;
413
414 for (i = 0; i < MAX_VIEWPORTS; i++) {
415 if (!viewports[i].initialized)
416 break;
417 }
418 if (i == MAX_VIEWPORTS)
419 return ELIMIT;
420
421 unsigned int cols = width / FONT_WIDTH;
422 unsigned int rows = height / FONT_SCANLINES;
423 unsigned int bbsize = cols * rows;
424 unsigned int glyphsize = 2 * FONT_GLYPHS * screen.glyphbytes;
425
426 uint8_t *backbuf = (uint8_t *) malloc(bbsize);
427 if (!backbuf)
428 return ENOMEM;
429
430 uint8_t *glyphs = (uint8_t *) malloc(glyphsize);
431 if (!glyphs) {
432 free(backbuf);
433 return ENOMEM;
434 }
435
436 uint8_t *bgpixel = (uint8_t *) malloc(screen.pixelbytes);
437 if (!bgpixel) {
438 free(glyphs);
439 free(backbuf);
440 return ENOMEM;
441 }
442
443 memset(backbuf, 0, bbsize);
444 memset(glyphs, 0, glyphsize);
445 memset(bgpixel, 0, screen.pixelbytes);
446
447 viewports[i].x = x;
448 viewports[i].y = y;
449 viewports[i].width = width;
450 viewports[i].height = height;
451
452 viewports[i].cols = cols;
453 viewports[i].rows = rows;
454
455 viewports[i].style.bg_color = DEFAULT_BGCOLOR;
456 viewports[i].style.fg_color = DEFAULT_FGCOLOR;
457
458 viewports[i].glyphs = glyphs;
459 viewports[i].bgpixel = bgpixel;
460
461 viewports[i].cur_col = 0;
462 viewports[i].cur_row = 0;
463 viewports[i].cursor_active = false;
464 viewports[i].cursor_shown = false;
465
466 viewports[i].bbsize = bbsize;
467 viewports[i].backbuf = backbuf;
468
469 viewports[i].initialized = true;
470
471 render_glyphs(&viewports[i]);
472
473 return i;
474}
475
476
477/** Initialize framebuffer as a chardev output device
478 *
479 * @param addr Address of the framebuffer
480 * @param xres Screen width in pixels
481 * @param yres Screen height in pixels
482 * @param visual Bits per pixel (8, 16, 24, 32)
483 * @param scan Bytes per one scanline
484 *
485 */
486static bool screen_init(void *addr, unsigned int xres, unsigned int yres,
487 unsigned int scan, unsigned int visual)
488{
489 switch (visual) {
490 case VISUAL_INDIRECT_8:
491 screen.rgb_conv = rgb_323;
492 screen.pixelbytes = 1;
493 break;
494 case VISUAL_RGB_5_5_5:
495 screen.rgb_conv = rgb_555;
496 screen.pixelbytes = 2;
497 break;
498 case VISUAL_RGB_5_6_5:
499 screen.rgb_conv = rgb_565;
500 screen.pixelbytes = 2;
501 break;
502 case VISUAL_RGB_8_8_8:
503 screen.rgb_conv = rgb_888;
504 screen.pixelbytes = 3;
505 break;
506 case VISUAL_RGB_8_8_8_0:
507 screen.rgb_conv = rgb_888;
508 screen.pixelbytes = 4;
509 break;
510 case VISUAL_RGB_0_8_8_8:
511 screen.rgb_conv = rgb_0888;
512 screen.pixelbytes = 4;
513 break;
514 case VISUAL_BGR_0_8_8_8:
515 screen.rgb_conv = bgr_0888;
516 screen.pixelbytes = 4;
517 break;
518 default:
519 return false;
520 }
521
522 screen.fb_addr = (unsigned char *) addr;
523 screen.xres = xres;
524 screen.yres = yres;
525 screen.scanline = scan;
526
527 screen.glyphscanline = FONT_WIDTH * screen.pixelbytes;
528 screen.glyphbytes = screen.glyphscanline * FONT_SCANLINES;
529
530 /* Create first viewport */
531 vport_create(0, 0, xres, yres);
532
533 return true;
534}
535
536
537/** Draw a glyph.
538 *
539 * @param x x coordinate of top-left corner on screen.
540 * @param y y coordinate of top-left corner on screen.
541 * @param cursor Draw glyph with cursor
542 * @param glyphs Pointer to font bitmap.
543 * @param glyph Code of the glyph to draw.
544 *
545 */
546static void draw_glyph(unsigned int x, unsigned int y, bool cursor,
547 uint8_t *glyphs, uint8_t glyph)
548{
549 unsigned int yd;
550
551 for (yd = 0; yd < FONT_SCANLINES; yd++)
552 memcpy(&screen.fb_addr[FB_POS(x, y + yd)],
553 &glyphs[GLYPH_POS(glyph, yd, cursor)], screen.glyphscanline);
554}
555
556/** Draw glyph at specified position in viewport.
557 *
558 * @param vport Viewport identification
559 * @param cursor Draw glyph with cursor
560 * @param col Screen position relative to viewport
561 * @param row Screen position relative to viewport
562 *
563 */
564static void draw_vp_glyph(viewport_t *vport, bool cursor, unsigned int col,
565 unsigned int row)
566{
567 unsigned int x = vport->x + COL2X(col);
568 unsigned int y = vport->y + ROW2Y(row);
569 uint8_t glyph;
570
571 glyph = vport->backbuf[BB_POS(vport, col, row)];
572 draw_glyph(x, y, cursor, vport->glyphs, glyph);
573}
574
575
576/** Hide cursor if it is shown
577 *
578 */
579static void cursor_hide(viewport_t *vport)
580{
581 if ((vport->cursor_active) && (vport->cursor_shown)) {
582 draw_vp_glyph(vport, false, vport->cur_col, vport->cur_row);
583 vport->cursor_shown = false;
584 }
585}
586
587
588/** Show cursor if cursor showing is enabled
589 *
590 */
591static void cursor_show(viewport_t *vport)
592{
593 /* Do not check for cursor_shown */
594 if (vport->cursor_active) {
595 draw_vp_glyph(vport, true, vport->cur_col, vport->cur_row);
596 vport->cursor_shown = true;
597 }
598}
599
600
601/** Invert cursor, if it is enabled
602 *
603 */
604static void cursor_blink(viewport_t *vport)
605{
606 if (vport->cursor_shown)
607 cursor_hide(vport);
608 else
609 cursor_show(vport);
610}
611
612
613/** Draw character at given position relative to viewport
614 *
615 * @param vport Viewport identification
616 * @param c Character to draw
617 * @param col Screen position relative to viewport
618 * @param row Screen position relative to viewport
619 *
620 */
621static void draw_char(viewport_t *vport, uint8_t c, unsigned int col, unsigned int row)
622{
623 /* Do not hide cursor if we are going to overwrite it */
624 if ((vport->cursor_active) && (vport->cursor_shown) &&
625 ((vport->cur_col != col) || (vport->cur_row != row)))
626 cursor_hide(vport);
627
628 vport->backbuf[BB_POS(vport, col, row)] = c;
629 draw_vp_glyph(vport, false, col, row);
630
631 vport->cur_col = col;
632 vport->cur_row = row;
633
634 vport->cur_col++;
635 if (vport->cur_col >= vport->cols) {
636 vport->cur_col = 0;
637 vport->cur_row++;
638 if (vport->cur_row >= vport->rows)
639 vport->cur_row--;
640 }
641
642 cursor_show(vport);
643}
644
645
646/** Draw text data to viewport
647 *
648 * @param vport Viewport id
649 * @param data Text data fitting exactly into viewport
650 *
651 */
652static void draw_text_data(viewport_t *vport, keyfield_t *data)
653{
654 unsigned int i;
655
656 for (i = 0; i < vport->cols * vport->rows; i++) {
657 unsigned int col = i % vport->cols;
658 unsigned int row = i / vport->cols;
659
660 uint8_t glyph = vport->backbuf[BB_POS(vport, col, row)];
661
662 // TODO: use data[i].style
663
664 if (glyph != data[i].character) {
665 vport->backbuf[BB_POS(vport, col, row)] = data[i].character;
666 draw_vp_glyph(vport, false, col, row);
667 }
668 }
669 cursor_show(vport);
670}
671
672
673static void putpixel_pixmap(void *data, unsigned int x, unsigned int y, uint32_t color)
674{
675 int pm = *((int *) data);
676 pixmap_t *pmap = &pixmaps[pm];
677 unsigned int pos = (y * pmap->width + x) * screen.pixelbytes;
678
679 screen.rgb_conv(&pmap->data[pos], color);
680}
681
682
683static void putpixel(void *data, unsigned int x, unsigned int y, uint32_t color)
684{
685 viewport_t *vport = (viewport_t *) data;
686 unsigned int dx = vport->x + x;
687 unsigned int dy = vport->y + y;
688
689 screen.rgb_conv(&screen.fb_addr[FB_POS(dx, dy)], color);
690}
691
692
693/** Return first free pixmap
694 *
695 */
696static int find_free_pixmap(void)
697{
698 unsigned int i;
699
700 for (i = 0; i < MAX_PIXMAPS; i++)
701 if (!pixmaps[i].data)
702 return i;
703
704 return -1;
705}
706
707
708/** Create a new pixmap and return appropriate ID
709 *
710 */
711static int shm2pixmap(unsigned char *shm, size_t size)
712{
713 int pm;
714 pixmap_t *pmap;
715
716 pm = find_free_pixmap();
717 if (pm == -1)
718 return ELIMIT;
719
720 pmap = &pixmaps[pm];
721
722 if (ppm_get_data(shm, size, &pmap->width, &pmap->height))
723 return EINVAL;
724
725 pmap->data = malloc(pmap->width * pmap->height * screen.pixelbytes);
726 if (!pmap->data)
727 return ENOMEM;
728
729 ppm_draw(shm, size, 0, 0, pmap->width, pmap->height, putpixel_pixmap, (void *) &pm);
730
731 return pm;
732}
733
734
735/** Handle shared memory communication calls
736 *
737 * Protocol for drawing pixmaps:
738 * - FB_PREPARE_SHM(client shm identification)
739 * - IPC_M_AS_AREA_SEND
740 * - FB_DRAW_PPM(startx, starty)
741 * - FB_DROP_SHM
742 *
743 * Protocol for text drawing
744 * - IPC_M_AS_AREA_SEND
745 * - FB_DRAW_TEXT_DATA
746 *
747 * @param callid Callid of the current call
748 * @param call Current call data
749 * @param vp Active viewport
750 *
751 * @return false if the call was not handled byt this function, true otherwise
752 *
753 * Note: this function is not threads safe, you would have
754 * to redefine static variables with __thread
755 *
756 */
757static bool shm_handle(ipc_callid_t callid, ipc_call_t *call, int vp)
758{
759 static keyfield_t *interbuffer = NULL;
760 static size_t intersize = 0;
761
762 static unsigned char *shm = NULL;
763 static ipcarg_t shm_id = 0;
764 static size_t shm_size;
765
766 bool handled = true;
767 int retval = EOK;
768 viewport_t *vport = &viewports[vp];
769 unsigned int x;
770 unsigned int y;
771
772 switch (IPC_GET_METHOD(*call)) {
773 case IPC_M_SHARE_OUT:
774 /* We accept one area for data interchange */
775 if (IPC_GET_ARG1(*call) == shm_id) {
776 void *dest = as_get_mappable_page(IPC_GET_ARG2(*call));
777 shm_size = IPC_GET_ARG2(*call);
778 if (!ipc_answer_1(callid, EOK, (sysarg_t) dest))
779 shm = dest;
780 else
781 shm_id = 0;
782
783 if (shm[0] != 'P')
784 return false;
785
786 return true;
787 } else {
788 intersize = IPC_GET_ARG2(*call);
789 receive_comm_area(callid, call, (void *) &interbuffer);
790 }
791 return true;
792 case FB_PREPARE_SHM:
793 if (shm_id)
794 retval = EBUSY;
795 else
796 shm_id = IPC_GET_ARG1(*call);
797 break;
798
799 case FB_DROP_SHM:
800 if (shm) {
801 as_area_destroy(shm);
802 shm = NULL;
803 }
804 shm_id = 0;
805 break;
806
807 case FB_SHM2PIXMAP:
808 if (!shm) {
809 retval = EINVAL;
810 break;
811 }
812 retval = shm2pixmap(shm, shm_size);
813 break;
814 case FB_DRAW_PPM:
815 if (!shm) {
816 retval = EINVAL;
817 break;
818 }
819 x = IPC_GET_ARG1(*call);
820 y = IPC_GET_ARG2(*call);
821
822 if ((x > vport->width) || (y > vport->height)) {
823 retval = EINVAL;
824 break;
825 }
826
827 ppm_draw(shm, shm_size, IPC_GET_ARG1(*call),
828 IPC_GET_ARG2(*call), vport->width - x, vport->height - y, putpixel, (void *) vport);
829 break;
830 case FB_DRAW_TEXT_DATA:
831 if (!interbuffer) {
832 retval = EINVAL;
833 break;
834 }
835 if (intersize < vport->cols * vport->rows * sizeof(*interbuffer)) {
836 retval = EINVAL;
837 break;
838 }
839 draw_text_data(vport, interbuffer);
840 break;
841 default:
842 handled = false;
843 }
844
845 if (handled)
846 ipc_answer_0(callid, retval);
847 return handled;
848}
849
850
851static void copy_vp_to_pixmap(viewport_t *vport, pixmap_t *pmap)
852{
853 unsigned int width = vport->width;
854 unsigned int height = vport->height;
855
856 if (width + vport->x > screen.xres)
857 width = screen.xres - vport->x;
858 if (height + vport->y > screen.yres)
859 height = screen.yres - vport->y;
860
861 unsigned int realwidth = pmap->width <= width ? pmap->width : width;
862 unsigned int realheight = pmap->height <= height ? pmap->height : height;
863
864 unsigned int srcrowsize = vport->width * screen.pixelbytes;
865 unsigned int realrowsize = realwidth * screen.pixelbytes;
866
867 unsigned int y;
868 for (y = 0; y < realheight; y++) {
869 unsigned int tmp = (vport->y + y) * screen.scanline + vport->x * screen.pixelbytes;
870 memcpy(pmap->data + srcrowsize * y, screen.fb_addr + tmp, realrowsize);
871 }
872}
873
874
875/** Save viewport to pixmap
876 *
877 */
878static int save_vp_to_pixmap(viewport_t *vport)
879{
880 int pm;
881 pixmap_t *pmap;
882
883 pm = find_free_pixmap();
884 if (pm == -1)
885 return ELIMIT;
886
887 pmap = &pixmaps[pm];
888 pmap->data = malloc(screen.pixelbytes * vport->width * vport->height);
889 if (!pmap->data)
890 return ENOMEM;
891
892 pmap->width = vport->width;
893 pmap->height = vport->height;
894
895 copy_vp_to_pixmap(vport, pmap);
896
897 return pm;
898}
899
900
901/** Draw pixmap on screen
902 *
903 * @param vp Viewport to draw on
904 * @param pm Pixmap identifier
905 *
906 */
907static int draw_pixmap(int vp, int pm)
908{
909 pixmap_t *pmap = &pixmaps[pm];
910 viewport_t *vport = &viewports[vp];
911
912 unsigned int width = vport->width;
913 unsigned int height = vport->height;
914
915 if (width + vport->x > screen.xres)
916 width = screen.xres - vport->x;
917 if (height + vport->y > screen.yres)
918 height = screen.yres - vport->y;
919
920 if (!pmap->data)
921 return EINVAL;
922
923 unsigned int realwidth = pmap->width <= width ? pmap->width : width;
924 unsigned int realheight = pmap->height <= height ? pmap->height : height;
925
926 unsigned int srcrowsize = vport->width * screen.pixelbytes;
927 unsigned int realrowsize = realwidth * screen.pixelbytes;
928
929 unsigned int y;
930 for (y = 0; y < realheight; y++) {
931 unsigned int tmp = (vport->y + y) * screen.scanline + vport->x * screen.pixelbytes;
932 memcpy(screen.fb_addr + tmp, pmap->data + y * srcrowsize, realrowsize);
933 }
934
935 return EOK;
936}
937
938
939/** Tick animation one step forward
940 *
941 */
942static void anims_tick(void)
943{
944 unsigned int i;
945 static int counts = 0;
946
947 /* Limit redrawing */
948 counts = (counts + 1) % 8;
949 if (counts)
950 return;
951
952 for (i = 0; i < MAX_ANIMATIONS; i++) {
953 if ((!animations[i].animlen) || (!animations[i].initialized) ||
954 (!animations[i].enabled))
955 continue;
956
957 draw_pixmap(animations[i].vp, animations[i].pixmaps[animations[i].pos]);
958 animations[i].pos = (animations[i].pos + 1) % animations[i].animlen;
959 }
960}
961
962
963static unsigned int pointer_x;
964static unsigned int pointer_y;
965static bool pointer_shown, pointer_enabled;
966static int pointer_vport = -1;
967static int pointer_pixmap = -1;
968
969
970static void mouse_show(void)
971{
972 int i, j;
973 int visibility;
974 int color;
975 int bytepos;
976
977 if ((pointer_shown) || (!pointer_enabled))
978 return;
979
980 /* Save image under the pointer. */
981 if (pointer_vport == -1) {
982 pointer_vport = vport_create(pointer_x, pointer_y, pointer_width, pointer_height);
983 if (pointer_vport < 0)
984 return;
985 } else {
986 viewports[pointer_vport].x = pointer_x;
987 viewports[pointer_vport].y = pointer_y;
988 }
989
990 if (pointer_pixmap == -1)
991 pointer_pixmap = save_vp_to_pixmap(&viewports[pointer_vport]);
992 else
993 copy_vp_to_pixmap(&viewports[pointer_vport], &pixmaps[pointer_pixmap]);
994
995 /* Draw mouse pointer. */
996 for (i = 0; i < pointer_height; i++)
997 for (j = 0; j < pointer_width; j++) {
998 bytepos = i * ((pointer_width - 1) / 8 + 1) + j / 8;
999 visibility = pointer_mask_bits[bytepos] &
1000 (1 << (j % 8));
1001 if (visibility) {
1002 color = pointer_bits[bytepos] &
1003 (1 << (j % 8)) ? 0 : 0xffffff;
1004 if (pointer_x + j < screen.xres && pointer_y +
1005 i < screen.yres)
1006 putpixel(&viewports[0], pointer_x + j,
1007 pointer_y + i, color);
1008 }
1009 }
1010 pointer_shown = 1;
1011}
1012
1013
1014static void mouse_hide(void)
1015{
1016 /* Restore image under the pointer. */
1017 if (pointer_shown) {
1018 draw_pixmap(pointer_vport, pointer_pixmap);
1019 pointer_shown = 0;
1020 }
1021}
1022
1023
1024static void mouse_move(unsigned int x, unsigned int y)
1025{
1026 mouse_hide();
1027 pointer_x = x;
1028 pointer_y = y;
1029 mouse_show();
1030}
1031
1032
1033static int anim_handle(ipc_callid_t callid, ipc_call_t *call, int vp)
1034{
1035 bool handled = true;
1036 int retval = EOK;
1037 int i, nvp;
1038 int newval;
1039
1040 switch (IPC_GET_METHOD(*call)) {
1041 case FB_ANIM_CREATE:
1042 nvp = IPC_GET_ARG1(*call);
1043 if (nvp == -1)
1044 nvp = vp;
1045 if (nvp >= MAX_VIEWPORTS || nvp < 0 ||
1046 !viewports[nvp].initialized) {
1047 retval = EINVAL;
1048 break;
1049 }
1050 for (i = 0; i < MAX_ANIMATIONS; i++) {
1051 if (!animations[i].initialized)
1052 break;
1053 }
1054 if (i == MAX_ANIMATIONS) {
1055 retval = ELIMIT;
1056 break;
1057 }
1058 animations[i].initialized = 1;
1059 animations[i].animlen = 0;
1060 animations[i].pos = 0;
1061 animations[i].enabled = 0;
1062 animations[i].vp = nvp;
1063 retval = i;
1064 break;
1065 case FB_ANIM_DROP:
1066 i = IPC_GET_ARG1(*call);
1067 if (i >= MAX_ANIMATIONS || i < 0) {
1068 retval = EINVAL;
1069 break;
1070 }
1071 animations[i].initialized = 0;
1072 break;
1073 case FB_ANIM_ADDPIXMAP:
1074 i = IPC_GET_ARG1(*call);
1075 if (i >= MAX_ANIMATIONS || i < 0 ||
1076 !animations[i].initialized) {
1077 retval = EINVAL;
1078 break;
1079 }
1080 if (animations[i].animlen == MAX_ANIM_LEN) {
1081 retval = ELIMIT;
1082 break;
1083 }
1084 newval = IPC_GET_ARG2(*call);
1085 if (newval < 0 || newval > MAX_PIXMAPS ||
1086 !pixmaps[newval].data) {
1087 retval = EINVAL;
1088 break;
1089 }
1090 animations[i].pixmaps[animations[i].animlen++] = newval;
1091 break;
1092 case FB_ANIM_CHGVP:
1093 i = IPC_GET_ARG1(*call);
1094 if (i >= MAX_ANIMATIONS || i < 0) {
1095 retval = EINVAL;
1096 break;
1097 }
1098 nvp = IPC_GET_ARG2(*call);
1099 if (nvp == -1)
1100 nvp = vp;
1101 if (nvp >= MAX_VIEWPORTS || nvp < 0 ||
1102 !viewports[nvp].initialized) {
1103 retval = EINVAL;
1104 break;
1105 }
1106 animations[i].vp = nvp;
1107 break;
1108 case FB_ANIM_START:
1109 case FB_ANIM_STOP:
1110 i = IPC_GET_ARG1(*call);
1111 if (i >= MAX_ANIMATIONS || i < 0) {
1112 retval = EINVAL;
1113 break;
1114 }
1115 newval = (IPC_GET_METHOD(*call) == FB_ANIM_START);
1116 if (newval ^ animations[i].enabled) {
1117 animations[i].enabled = newval;
1118 anims_enabled += newval ? 1 : -1;
1119 }
1120 break;
1121 default:
1122 handled = 0;
1123 }
1124 if (handled)
1125 ipc_answer_0(callid, retval);
1126 return handled;
1127}
1128
1129
1130/** Handler for messages concerning pixmap handling
1131 *
1132 */
1133static int pixmap_handle(ipc_callid_t callid, ipc_call_t *call, int vp)
1134{
1135 bool handled = true;
1136 int retval = EOK;
1137 int i, nvp;
1138
1139 switch (IPC_GET_METHOD(*call)) {
1140 case FB_VP_DRAW_PIXMAP:
1141 nvp = IPC_GET_ARG1(*call);
1142 if (nvp == -1)
1143 nvp = vp;
1144 if (nvp < 0 || nvp >= MAX_VIEWPORTS ||
1145 !viewports[nvp].initialized) {
1146 retval = EINVAL;
1147 break;
1148 }
1149 i = IPC_GET_ARG2(*call);
1150 retval = draw_pixmap(nvp, i);
1151 break;
1152 case FB_VP2PIXMAP:
1153 nvp = IPC_GET_ARG1(*call);
1154 if (nvp == -1)
1155 nvp = vp;
1156 if (nvp < 0 || nvp >= MAX_VIEWPORTS ||
1157 !viewports[nvp].initialized)
1158 retval = EINVAL;
1159 else
1160 retval = save_vp_to_pixmap(&viewports[nvp]);
1161 break;
1162 case FB_DROP_PIXMAP:
1163 i = IPC_GET_ARG1(*call);
1164 if (i >= MAX_PIXMAPS) {
1165 retval = EINVAL;
1166 break;
1167 }
1168 if (pixmaps[i].data) {
1169 free(pixmaps[i].data);
1170 pixmaps[i].data = NULL;
1171 }
1172 break;
1173 default:
1174 handled = 0;
1175 }
1176
1177 if (handled)
1178 ipc_answer_0(callid, retval);
1179 return handled;
1180
1181}
1182
1183/** Function for handling connections to FB
1184 *
1185 */
1186static void fb_client_connection(ipc_callid_t iid, ipc_call_t *icall)
1187{
1188 unsigned int vp = 0;
1189 viewport_t *vport = &viewports[vp];
1190
1191 if (client_connected) {
1192 ipc_answer_0(iid, ELIMIT);
1193 return;
1194 }
1195
1196 /* Accept connection */
1197 client_connected = true;
1198 ipc_answer_0(iid, EOK);
1199
1200 while (true) {
1201 ipc_callid_t callid;
1202 ipc_call_t call;
1203 int retval;
1204 unsigned int i;
1205 int scroll;
1206 uint8_t glyph;
1207 unsigned int row, col;
1208
1209 if ((vport->cursor_active) || (anims_enabled))
1210 callid = async_get_call_timeout(&call, 250000);
1211 else
1212 callid = async_get_call(&call);
1213
1214 mouse_hide();
1215 if (!callid) {
1216 cursor_blink(vport);
1217 anims_tick();
1218 mouse_show();
1219 continue;
1220 }
1221
1222 if (shm_handle(callid, &call, vp))
1223 continue;
1224
1225 if (pixmap_handle(callid, &call, vp))
1226 continue;
1227
1228 if (anim_handle(callid, &call, vp))
1229 continue;
1230
1231 switch (IPC_GET_METHOD(call)) {
1232 case IPC_M_PHONE_HUNGUP:
1233 client_connected = false;
1234
1235 /* Cleanup other viewports */
1236 for (i = 1; i < MAX_VIEWPORTS; i++)
1237 vport->initialized = false;
1238
1239 /* Exit thread */
1240 return;
1241
1242 case FB_PUTCHAR:
1243 glyph = IPC_GET_ARG1(call);
1244 row = IPC_GET_ARG2(call);
1245 col = IPC_GET_ARG3(call);
1246
1247 if ((col >= vport->cols) || (row >= vport->rows)) {
1248 retval = EINVAL;
1249 break;
1250 }
1251 ipc_answer_0(callid, EOK);
1252
1253 draw_char(vport, glyph, col, row);
1254
1255 /* Message already answered */
1256 continue;
1257 case FB_CLEAR:
1258 vport_clear(vport);
1259 cursor_show(vport);
1260 retval = EOK;
1261 break;
1262 case FB_CURSOR_GOTO:
1263 row = IPC_GET_ARG1(call);
1264 col = IPC_GET_ARG2(call);
1265
1266 if ((col >= vport->cols) || (row >= vport->rows)) {
1267 retval = EINVAL;
1268 break;
1269 }
1270 retval = EOK;
1271
1272 cursor_hide(vport);
1273 vport->cur_col = col;
1274 vport->cur_row = row;
1275 cursor_show(vport);
1276 break;
1277 case FB_CURSOR_VISIBILITY:
1278 cursor_hide(vport);
1279 vport->cursor_active = IPC_GET_ARG1(call);
1280 cursor_show(vport);
1281 retval = EOK;
1282 break;
1283 case FB_GET_CSIZE:
1284 ipc_answer_2(callid, EOK, vport->rows, vport->cols);
1285 continue;
1286 case FB_SCROLL:
1287 scroll = IPC_GET_ARG1(call);
1288 if ((scroll > (int) vport->rows) || (scroll < (-(int) vport->rows))) {
1289 retval = EINVAL;
1290 break;
1291 }
1292 cursor_hide(vport);
1293 vport_scroll(vport, scroll);
1294 cursor_show(vport);
1295 retval = EOK;
1296 break;
1297 case FB_VIEWPORT_SWITCH:
1298 i = IPC_GET_ARG1(call);
1299 if (i >= MAX_VIEWPORTS) {
1300 retval = EINVAL;
1301 break;
1302 }
1303 if (!viewports[i].initialized) {
1304 retval = EADDRNOTAVAIL;
1305 break;
1306 }
1307 cursor_hide(vport);
1308 vp = i;
1309 vport = &viewports[vp];
1310 cursor_show(vport);
1311 retval = EOK;
1312 break;
1313 case FB_VIEWPORT_CREATE:
1314 retval = vport_create(IPC_GET_ARG1(call) >> 16,
1315 IPC_GET_ARG1(call) & 0xffff,
1316 IPC_GET_ARG2(call) >> 16,
1317 IPC_GET_ARG2(call) & 0xffff);
1318 break;
1319 case FB_VIEWPORT_DELETE:
1320 i = IPC_GET_ARG1(call);
1321 if (i >= MAX_VIEWPORTS) {
1322 retval = EINVAL;
1323 break;
1324 }
1325 if (!viewports[i].initialized) {
1326 retval = EADDRNOTAVAIL;
1327 break;
1328 }
1329 viewports[i].initialized = false;
1330 if (viewports[i].glyphs)
1331 free(viewports[i].glyphs);
1332 if (viewports[i].bgpixel)
1333 free(viewports[i].bgpixel);
1334 if (viewports[i].backbuf)
1335 free(viewports[i].backbuf);
1336 retval = EOK;
1337 break;
1338 case FB_SET_STYLE:
1339 vport->style.fg_color = IPC_GET_ARG1(call);
1340 vport->style.bg_color = IPC_GET_ARG2(call);
1341 render_glyphs(vport);
1342 retval = EOK;
1343 break;
1344 case FB_GET_RESOLUTION:
1345 ipc_answer_2(callid, EOK, screen.xres, screen.yres);
1346 continue;
1347 case FB_POINTER_MOVE:
1348 pointer_enabled = true;
1349 mouse_move(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
1350 retval = EOK;
1351 break;
1352 default:
1353 retval = ENOENT;
1354 }
1355 ipc_answer_0(callid, retval);
1356 }
1357}
1358
1359/** Initialization of framebuffer
1360 *
1361 */
1362int fb_init(void)
1363{
1364 async_set_client_connection(fb_client_connection);
1365
1366 void *fb_ph_addr = (void *) sysinfo_value("fb.address.physical");
1367 unsigned int fb_offset = sysinfo_value("fb.offset");
1368 unsigned int fb_width = sysinfo_value("fb.width");
1369 unsigned int fb_height = sysinfo_value("fb.height");
1370 unsigned int fb_scanline = sysinfo_value("fb.scanline");
1371 unsigned int fb_visual = sysinfo_value("fb.visual");
1372
1373 unsigned int fbsize = fb_scanline * fb_height;
1374 void *fb_addr = as_get_mappable_page(fbsize);
1375
1376 physmem_map(fb_ph_addr + fb_offset, fb_addr,
1377 ALIGN_UP(fbsize, PAGE_SIZE) >> PAGE_WIDTH, AS_AREA_READ | AS_AREA_WRITE);
1378
1379 if (screen_init(fb_addr, fb_width, fb_height, fb_scanline, fb_visual))
1380 return 0;
1381
1382 return -1;
1383}
1384
1385/**
1386 * @}
1387 */
Note: See TracBrowser for help on using the repository browser.