Changes in uspace/srv/hid/fb/fb.c [ffa2c8ef:7c014d1] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/srv/hid/fb/fb.c
rffa2c8ef r7c014d1 1 1 /* 2 * Copyright (c) 2008 Martin Decky 3 * Copyright (c) 2006 Jakub Vana 4 * Copyright (c) 2006 Ondrej Palkovsky 2 * Copyright (c) 2011 Martin Decky 5 3 * All rights reserved. 6 4 * … … 29 27 */ 30 28 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 <str.h> 44 #include <ddi.h> 45 #include <sysinfo.h> 46 #include <align.h> 29 #include <sys/types.h> 30 #include <bool.h> 31 #include <loc.h> 32 #include <errno.h> 33 #include <stdio.h> 34 #include <malloc.h> 35 #include <inttypes.h> 47 36 #include <as.h> 48 #include <ipc/fb.h> 49 #include <ipc/ns.h> 50 #include <ipc/services.h> 51 #include <kernel/errno.h> 52 #include <kernel/genarch/fb/visuals.h> 53 #include <io/color.h> 54 #include <io/style.h> 55 #include <async.h> 56 #include <fibril.h> 57 #include <bool.h> 58 #include <stdio.h> 59 #include <byteorder.h> 60 #include <io/screenbuffer.h> 61 62 #include "font-8x16.h" 37 #include <fb.h> 38 #include <screenbuffer.h> 39 #include "port/ega.h" 40 #include "port/kchar.h" 41 #include "port/kfb.h" 42 #include "port/niagara.h" 43 #include "port/ski.h" 63 44 #include "fb.h" 64 #include "main.h" 65 #include "ppm.h" 66 67 #include "pointer.xbm" 68 #include "pointer_mask.xbm" 69 70 #define DEFAULT_BGCOLOR 0xf0f0f0 71 #define DEFAULT_FGCOLOR 0x000000 72 73 #define GLYPH_UNAVAIL '?' 74 75 #define MAX_ANIM_LEN 8 76 #define MAX_ANIMATIONS 4 77 #define MAX_PIXMAPS 256 /**< Maximum number of saved pixmaps */ 78 #define MAX_VIEWPORTS 128 /**< Viewport is a rectangular area on the screen */ 79 80 /** Function to render a pixel from a RGB value. */ 81 typedef void (*rgb_conv_t)(void *, uint32_t); 82 83 /** Function to render a bit mask. */ 84 typedef void (*mask_conv_t)(void *, bool); 85 86 /** Function to draw a glyph. */ 87 typedef void (*dg_t)(unsigned int x, unsigned int y, bool cursor, 88 uint8_t *glyphs, uint32_t glyph, uint32_t fg_color, uint32_t bg_color); 89 90 struct { 91 uint8_t *fb_addr; 92 93 unsigned int xres; 94 unsigned int yres; 95 96 unsigned int scanline; 97 unsigned int glyphscanline; 98 99 unsigned int pixelbytes; 100 unsigned int glyphbytes; 101 102 /** Pre-rendered mask for rendering glyphs. Specific for the visual. */ 103 uint8_t *glyphs; 104 105 rgb_conv_t rgb_conv; 106 mask_conv_t mask_conv; 107 } screen; 108 109 /** Backbuffer character cell. */ 110 typedef struct { 111 uint32_t glyph; 112 uint32_t fg_color; 113 uint32_t bg_color; 114 } bb_cell_t; 115 116 typedef struct { 117 bool initialized; 118 unsigned int x; 119 unsigned int y; 120 unsigned int width; 121 unsigned int height; 122 123 /* Text support in window */ 124 unsigned int cols; 125 unsigned int rows; 45 46 #define NAME "fb" 47 #define NAMESPACE "hid" 48 49 #define TICK_INTERVAL 250000 50 51 static LIST_INITIALIZE(fbdevs); 52 53 fbdev_t *fbdev_register(fbdev_ops_t *ops, void *data) 54 { 55 sysarg_t index = 0; 56 57 if (!list_empty(&fbdevs)) { 58 list_foreach(fbdevs, link) { 59 fbdev_t *dev = list_get_instance(link, fbdev_t, link); 60 if (index <= dev->index) 61 index = dev->index + 1; 62 } 63 } 64 65 fbdev_t *dev = (fbdev_t *) malloc(sizeof(fbdev_t)); 66 if (dev == NULL) 67 return NULL; 68 69 link_initialize(&dev->link); 70 atomic_set(&dev->refcnt, 0); 71 dev->claimed = false; 72 dev->index = index; 73 list_initialize(&dev->vps); 74 list_initialize(&dev->frontbufs); 75 list_initialize(&dev->imagemaps); 76 list_initialize(&dev->sequences); 77 78 dev->ops = *ops; 79 dev->data = data; 80 81 char node[LOC_NAME_MAXLEN + 1]; 82 snprintf(node, LOC_NAME_MAXLEN, "%s/%s%" PRIun, NAMESPACE, NAME, 83 index); 84 85 if (loc_service_register(node, &dev->dsid) != EOK) { 86 printf("%s: Unable to register device %s\n", NAME, node); 87 free(dev); 88 return NULL; 89 } 90 91 list_append(&dev->link, &fbdevs); 92 return dev; 93 } 94 95 static void fbsrv_yield(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 96 { 97 assert(dev->ops.yield); 98 99 if (dev->claimed) { 100 int rc = dev->ops.yield(dev); 101 if (rc == EOK) 102 dev->claimed = false; 103 104 async_answer_0(iid, rc); 105 } else 106 async_answer_0(iid, ENOENT); 107 } 108 109 static void fbsrv_claim(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 110 { 111 assert(dev->ops.claim); 112 113 if (!dev->claimed) { 114 int rc = dev->ops.claim(dev); 115 if (rc == EOK) 116 dev->claimed = true; 117 118 async_answer_0(iid, rc); 119 } else 120 async_answer_0(iid, ENOENT); 121 } 122 123 static void fbsrv_get_resolution(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 124 { 125 assert(dev->ops.get_resolution); 126 127 sysarg_t width; 128 sysarg_t height; 129 int rc = dev->ops.get_resolution(dev, &width, &height); 130 131 async_answer_2(iid, rc, width, height); 132 } 133 134 static void fbsrv_pointer_update(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 135 { 136 if ((dev->claimed) && (dev->ops.pointer_update)) { 137 dev->ops.pointer_update(dev, IPC_GET_ARG1(*icall), 138 IPC_GET_ARG2(*icall), IPC_GET_ARG3(*icall)); 139 async_answer_0(iid, EOK); 140 } else 141 async_answer_0(iid, ENOTSUP); 142 } 143 144 static fbvp_t *resolve_vp(fbdev_t *dev, sysarg_t handle, ipc_callid_t iid) 145 { 146 fbvp_t *vp = NULL; 147 list_foreach(dev->vps, link) { 148 fbvp_t *cur = list_get_instance(link, fbvp_t, link); 149 if (cur == (fbvp_t *) handle) { 150 vp = cur; 151 break; 152 } 153 } 154 155 if (vp == NULL) { 156 async_answer_0(iid, ENOENT); 157 return NULL; 158 } 159 160 return vp; 161 } 162 163 static frontbuf_t *resolve_frontbuf(fbdev_t *dev, sysarg_t handle, ipc_callid_t iid) 164 { 165 frontbuf_t *frontbuf = NULL; 166 list_foreach(dev->frontbufs, link) { 167 frontbuf_t *cur = list_get_instance(link, frontbuf_t, link); 168 if (cur == (frontbuf_t *) handle) { 169 frontbuf = cur; 170 break; 171 } 172 } 173 174 if (frontbuf == NULL) { 175 async_answer_0(iid, ENOENT); 176 return NULL; 177 } 178 179 return frontbuf; 180 } 181 182 static imagemap_t *resolve_imagemap(fbdev_t *dev, sysarg_t handle, ipc_callid_t iid) 183 { 184 imagemap_t *imagemap = NULL; 185 list_foreach(dev->imagemaps, link) { 186 imagemap_t *cur = list_get_instance(link, imagemap_t, link); 187 if (cur == (imagemap_t *) handle) { 188 imagemap = cur; 189 break; 190 } 191 } 192 193 if (imagemap == NULL) { 194 async_answer_0(iid, ENOENT); 195 return NULL; 196 } 197 198 return imagemap; 199 } 200 201 static sequence_t *resolve_sequence(fbdev_t *dev, sysarg_t handle, ipc_callid_t iid) 202 { 203 sequence_t *sequence = NULL; 204 list_foreach(dev->sequences, link) { 205 sequence_t *cur = list_get_instance(link, sequence_t, link); 206 if (cur == (sequence_t *) handle) { 207 sequence = cur; 208 break; 209 } 210 } 211 212 if (sequence == NULL) { 213 async_answer_0(iid, ENOENT); 214 return NULL; 215 } 216 217 return sequence; 218 } 219 220 static void fbsrv_vp_create(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 221 { 222 assert(dev->ops.font_metrics); 223 assert(dev->ops.vp_create); 224 225 fbvp_t *vp = (fbvp_t *) malloc(sizeof(fbvp_t)); 226 if (vp == NULL) { 227 async_answer_0(iid, ENOMEM); 228 return; 229 } 230 231 link_initialize(&vp->link); 232 233 vp->x = IPC_GET_ARG1(*icall); 234 vp->y = IPC_GET_ARG2(*icall); 235 vp->width = IPC_GET_ARG3(*icall); 236 vp->height = IPC_GET_ARG4(*icall); 237 238 dev->ops.font_metrics(dev, vp->width, vp->height, &vp->cols, &vp->rows); 239 240 vp->cursor_active = false; 241 vp->cursor_flash = false; 242 243 list_initialize(&vp->sequences); 244 245 vp->backbuf = screenbuffer_create(vp->cols, vp->rows, 246 SCREENBUFFER_FLAG_NONE); 247 if (vp->backbuf == NULL) { 248 free(vp); 249 async_answer_0(iid, ENOMEM); 250 return; 251 } 252 253 vp->top_row = 0; 254 255 int rc = dev->ops.vp_create(dev, vp); 256 if (rc != EOK) { 257 free(vp); 258 async_answer_0(iid, ENOMEM); 259 return; 260 } 261 262 list_append(&vp->link, &dev->vps); 263 async_answer_1(iid, EOK, (sysarg_t) vp); 264 } 265 266 static void fbsrv_vp_destroy(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 267 { 268 assert(dev->ops.vp_destroy); 269 270 fbvp_t *vp = resolve_vp(dev, IPC_GET_ARG1(*icall), iid); 271 if (vp == NULL) 272 return; 273 274 if (dev->active_vp == vp) { 275 async_answer_0(iid, EPERM); 276 return; 277 } 278 279 dev->ops.vp_destroy(dev, vp); 280 281 list_remove(&vp->link); 282 free(vp->backbuf); 283 free(vp); 284 285 async_answer_0(iid, EOK); 286 } 287 288 static void fbsrv_frontbuf_create(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 289 { 290 frontbuf_t *frontbuf = (frontbuf_t *) malloc(sizeof(frontbuf_t)); 291 if (frontbuf == NULL) { 292 async_answer_0(iid, ENOMEM); 293 return; 294 } 295 296 link_initialize(&frontbuf->link); 297 298 ipc_callid_t callid; 299 if (!async_share_out_receive(&callid, &frontbuf->size, 300 &frontbuf->flags)) { 301 free(frontbuf); 302 async_answer_0(iid, EINVAL); 303 return; 304 } 305 306 frontbuf->data = as_get_mappable_page(frontbuf->size); 307 int rc = async_answer_1(callid, EOK, (sysarg_t) frontbuf->data); 308 if (rc != EOK) { 309 free(frontbuf); 310 async_answer_0(iid, ENOMEM); 311 return; 312 } 313 314 list_append(&frontbuf->link, &dev->frontbufs); 315 async_answer_1(iid, EOK, (sysarg_t) frontbuf); 316 } 317 318 static void fbsrv_frontbuf_destroy(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 319 { 320 frontbuf_t *frontbuf = resolve_frontbuf(dev, IPC_GET_ARG1(*icall), iid); 321 if (frontbuf == NULL) 322 return; 323 324 list_remove(&frontbuf->link); 325 as_area_destroy(frontbuf->data); 326 free(frontbuf); 327 328 async_answer_0(iid, EOK); 329 } 330 331 static void fbsrv_imagemap_create(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 332 { 333 imagemap_t *imagemap = (imagemap_t *) malloc(sizeof(imagemap_t)); 334 if (imagemap == NULL) { 335 async_answer_0(iid, ENOMEM); 336 return; 337 } 338 339 link_initialize(&imagemap->link); 340 link_initialize(&imagemap->seq_link); 341 342 ipc_callid_t callid; 343 if (!async_share_out_receive(&callid, &imagemap->size, 344 &imagemap->flags)) { 345 free(imagemap); 346 async_answer_0(iid, EINVAL); 347 return; 348 } 349 350 imagemap->data = as_get_mappable_page(imagemap->size); 351 int rc = async_answer_1(callid, EOK, (sysarg_t) imagemap->data); 352 if (rc != EOK) { 353 free(imagemap); 354 async_answer_0(iid, ENOMEM); 355 return; 356 } 357 358 list_append(&imagemap->link, &dev->imagemaps); 359 async_answer_1(iid, EOK, (sysarg_t) imagemap); 360 } 361 362 static void fbsrv_imagemap_destroy(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 363 { 364 imagemap_t *imagemap = resolve_imagemap(dev, IPC_GET_ARG1(*icall), iid); 365 if (imagemap == NULL) 366 return; 367 368 list_remove(&imagemap->link); 369 list_remove(&imagemap->seq_link); 370 as_area_destroy(imagemap->data); 371 free(imagemap); 372 373 async_answer_0(iid, EOK); 374 } 375 376 static void fbsrv_sequence_create(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 377 { 378 sequence_t *sequence = (sequence_t *) malloc(sizeof(sequence_t)); 379 if (sequence == NULL) { 380 async_answer_0(iid, ENOMEM); 381 return; 382 } 383 384 link_initialize(&sequence->link); 385 list_initialize(&sequence->imagemaps); 386 sequence->count = 0; 387 388 list_append(&sequence->link, &dev->sequences); 389 async_answer_1(iid, EOK, (sysarg_t) sequence); 390 } 391 392 static void fbsrv_sequence_destroy(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 393 { 394 sequence_t *sequence = resolve_sequence(dev, IPC_GET_ARG1(*icall), iid); 395 if (sequence == NULL) 396 return; 397 398 list_remove(&sequence->link); 399 free(sequence); 400 401 async_answer_0(iid, EOK); 402 } 403 404 static void fbsrv_sequence_add_imagemap(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 405 { 406 sequence_t *sequence = resolve_sequence(dev, IPC_GET_ARG1(*icall), iid); 407 if (sequence == NULL) 408 return; 409 410 imagemap_t *imagemap = resolve_imagemap(dev, IPC_GET_ARG2(*icall), iid); 411 if (imagemap == NULL) 412 return; 413 414 if (list_member(&imagemap->seq_link, &sequence->imagemaps)) { 415 async_answer_0(iid, EEXISTS); 416 return; 417 } 418 419 list_append(&imagemap->seq_link, &sequence->imagemaps); 420 sequence->count++; 421 422 async_answer_0(iid, EOK); 423 } 424 425 static void fbsrv_vp_focus(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 426 { 427 fbvp_t *vp = resolve_vp(dev, IPC_GET_ARG1(*icall), iid); 428 if (vp == NULL) 429 return; 430 431 if (dev->active_vp != vp) 432 dev->active_vp = vp; 433 434 async_answer_0(iid, EOK); 435 } 436 437 static void fbsrv_vp_clear(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 438 { 439 assert(dev->ops.vp_clear); 440 441 if ((dev->claimed) && (dev->active_vp)) { 442 screenbuffer_set_cursor_visibility(dev->active_vp->backbuf, false); 443 dev->ops.vp_clear(dev, dev->active_vp); 444 async_answer_0(iid, EOK); 445 } else 446 async_answer_0(iid, ENOENT); 447 } 448 449 static void fbsrv_vp_get_dimensions(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 450 { 451 if (dev->active_vp) 452 async_answer_2(iid, EOK, dev->active_vp->cols, dev->active_vp->rows); 453 else 454 async_answer_0(iid, ENOENT); 455 } 456 457 static void fbsrv_vp_get_caps(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 458 { 459 assert(dev->ops.vp_get_caps); 460 461 if (dev->active_vp) 462 async_answer_1(iid, EOK, 463 (sysarg_t) dev->ops.vp_get_caps(dev, dev->active_vp)); 464 else 465 async_answer_0(iid, ENOENT); 466 } 467 468 static void fbsrv_vp_cursor_update(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 469 { 470 assert(dev->ops.vp_cursor_update); 471 472 frontbuf_t *frontbuf = resolve_frontbuf(dev, IPC_GET_ARG1(*icall), iid); 473 if (frontbuf == NULL) 474 return; 475 476 if ((dev->claimed) && (dev->active_vp)) { 477 screenbuffer_t *screenbuf = (screenbuffer_t *) frontbuf->data; 478 479 sysarg_t prev_col; 480 sysarg_t prev_row; 481 sysarg_t col; 482 sysarg_t row; 483 484 screenbuffer_get_cursor(dev->active_vp->backbuf, 485 &prev_col, &prev_row); 486 screenbuffer_get_cursor(screenbuf, &col, &row); 487 screenbuffer_set_cursor(dev->active_vp->backbuf, col, row); 488 489 bool visible = screenbuffer_get_cursor_visibility(screenbuf); 490 screenbuffer_set_cursor_visibility(dev->active_vp->backbuf, visible); 491 492 if (visible) 493 dev->active_vp->cursor_active = true; 494 495 dev->ops.vp_cursor_update(dev, dev->active_vp, prev_col, prev_row, 496 col, row, visible); 497 async_answer_0(iid, EOK); 498 } else 499 async_answer_0(iid, ENOENT); 500 } 501 502 static void fbsrv_vp_cursor_flash(fbdev_t *dev) 503 { 504 if ((dev->claimed) && (dev->ops.vp_cursor_flash)) { 505 list_foreach (dev->vps, link) { 506 fbvp_t *vp = list_get_instance(link, fbvp_t, link); 507 508 if (vp->cursor_active) { 509 sysarg_t col; 510 sysarg_t row; 511 512 screenbuffer_get_cursor(vp->backbuf, &col, &row); 513 vp->cursor_flash = !vp->cursor_flash; 514 dev->ops.vp_cursor_flash(dev, vp, col, row); 515 } 516 } 517 } 518 } 519 520 static void fbsrv_sequences_update(fbdev_t *dev) 521 { 522 if ((dev->claimed) && (dev->ops.vp_imgmap_damage)) { 523 list_foreach (dev->vps, vp_link) { 524 fbvp_t *vp = list_get_instance(vp_link, fbvp_t, link); 525 526 list_foreach (vp->sequences, seq_vp_link) { 527 sequence_vp_t *seq_vp = 528 list_get_instance(seq_vp_link, sequence_vp_t, link); 529 530 seq_vp->current++; 531 if (seq_vp->current >= seq_vp->seq->count) 532 seq_vp->current = 0; 533 534 link_t *link = 535 list_nth(&seq_vp->seq->imagemaps, seq_vp->current); 536 if (link != NULL) { 537 imagemap_t *imagemap = 538 list_get_instance(link, imagemap_t, seq_link); 539 540 imgmap_t *imgmap = (imgmap_t *) imagemap->data; 541 sysarg_t width; 542 sysarg_t height; 543 544 imgmap_get_resolution(imgmap, &width, &height); 545 dev->ops.vp_imgmap_damage(dev, vp, imgmap, 546 0, 0, width, height); 547 } 548 } 549 } 550 } 551 } 552 553 static void fbsrv_vp_set_style(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 554 { 555 if (dev->active_vp) { 556 dev->active_vp->attrs.type = CHAR_ATTR_STYLE; 557 dev->active_vp->attrs.val.style = 558 (console_style_t) IPC_GET_ARG1(*icall); 559 async_answer_0(iid, EOK); 560 } else 561 async_answer_0(iid, ENOENT); 562 } 563 564 static void fbsrv_vp_set_color(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 565 { 566 if (dev->active_vp) { 567 dev->active_vp->attrs.type = CHAR_ATTR_INDEX; 568 dev->active_vp->attrs.val.index.bgcolor = 569 (console_color_t) IPC_GET_ARG1(*icall); 570 dev->active_vp->attrs.val.index.fgcolor = 571 (console_color_t) IPC_GET_ARG2(*icall); 572 dev->active_vp->attrs.val.index.attr = 573 (console_color_attr_t) IPC_GET_ARG3(*icall); 574 async_answer_0(iid, EOK); 575 } else 576 async_answer_0(iid, ENOENT); 577 } 578 579 static void fbsrv_vp_set_rgb_color(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 580 { 581 if (dev->active_vp) { 582 dev->active_vp->attrs.type = CHAR_ATTR_RGB; 583 dev->active_vp->attrs.val.rgb.bgcolor = IPC_GET_ARG1(*icall); 584 dev->active_vp->attrs.val.rgb.fgcolor = IPC_GET_ARG2(*icall); 585 async_answer_0(iid, EOK); 586 } else 587 async_answer_0(iid, ENOENT); 588 } 589 590 static void fbsrv_vp_putchar(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 591 { 592 assert(dev->ops.vp_char_update); 593 594 if ((dev->claimed) && (dev->active_vp)) { 595 charfield_t *field = screenbuffer_field_at(dev->active_vp->backbuf, 596 IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall)); 597 598 field->ch = IPC_GET_ARG3(*icall); 599 600 dev->ops.vp_char_update(dev, dev->active_vp, IPC_GET_ARG1(*icall), 601 IPC_GET_ARG2(*icall)); 602 async_answer_0(iid, EOK); 603 } else 604 async_answer_0(iid, ENOENT); 605 } 606 607 static bool fbsrv_vp_update_scroll(fbdev_t *dev, fbvp_t *vp, 608 screenbuffer_t *frontbuf) 609 { 610 assert(dev->ops.vp_char_update); 611 612 sysarg_t top_row = screenbuffer_get_top_row(frontbuf); 613 614 if (vp->top_row == top_row) 615 return false; 616 617 vp->top_row = top_row; 618 619 for (sysarg_t y = 0; y < vp->rows; y++) { 620 for (sysarg_t x = 0; x < vp->cols; x++) { 621 charfield_t *front_field = 622 screenbuffer_field_at(frontbuf, x, y); 623 charfield_t *back_field = 624 screenbuffer_field_at(vp->backbuf, x, y); 625 bool update = false; 626 627 if (front_field->ch != back_field->ch) { 628 back_field->ch = front_field->ch; 629 update = true; 630 } 631 632 if (!attrs_same(front_field->attrs, back_field->attrs)) { 633 back_field->attrs = front_field->attrs; 634 update = true; 635 } 636 637 front_field->flags &= ~CHAR_FLAG_DIRTY; 638 639 if (update) 640 dev->ops.vp_char_update(dev, vp, x, y); 641 } 642 } 643 644 return true; 645 } 646 647 static void fbsrv_vp_update(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 648 { 649 assert(dev->ops.vp_char_update); 650 651 frontbuf_t *frontbuf = resolve_frontbuf(dev, IPC_GET_ARG1(*icall), iid); 652 if (frontbuf == NULL) 653 return; 654 655 if ((dev->claimed) && (dev->active_vp)) { 656 fbvp_t *vp = dev->active_vp; 657 screenbuffer_t *screenbuf = (screenbuffer_t *) frontbuf->data; 658 659 if (fbsrv_vp_update_scroll(dev, vp, screenbuf)) { 660 async_answer_0(iid, EOK); 661 return; 662 } 663 664 for (sysarg_t y = 0; y < vp->rows; y++) { 665 for (sysarg_t x = 0; x < vp->cols; x++) { 666 charfield_t *front_field = 667 screenbuffer_field_at(screenbuf, x, y); 668 charfield_t *back_field = 669 screenbuffer_field_at(vp->backbuf, x, y); 670 bool update = false; 671 672 if ((front_field->flags & CHAR_FLAG_DIRTY) == CHAR_FLAG_DIRTY) { 673 if (front_field->ch != back_field->ch) { 674 back_field->ch = front_field->ch; 675 update = true; 676 } 677 678 if (!attrs_same(front_field->attrs, back_field->attrs)) { 679 back_field->attrs = front_field->attrs; 680 update = true; 681 } 682 683 front_field->flags &= ~CHAR_FLAG_DIRTY; 684 } 685 686 if (update) 687 dev->ops.vp_char_update(dev, vp, x, y); 688 } 689 } 690 691 async_answer_0(iid, EOK); 692 } else 693 async_answer_0(iid, ENOENT); 694 } 695 696 static void fbsrv_vp_damage(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 697 { 698 assert(dev->ops.vp_char_update); 699 700 frontbuf_t *frontbuf = resolve_frontbuf(dev, IPC_GET_ARG1(*icall), iid); 701 if (frontbuf == NULL) 702 return; 703 704 if ((dev->claimed) && (dev->active_vp)) { 705 fbvp_t *vp = dev->active_vp; 706 screenbuffer_t *screenbuf = (screenbuffer_t *) frontbuf->data; 707 708 if (fbsrv_vp_update_scroll(dev, vp, screenbuf)) { 709 async_answer_0(iid, EOK); 710 return; 711 } 712 713 sysarg_t col = IPC_GET_ARG2(*icall); 714 sysarg_t row = IPC_GET_ARG3(*icall); 715 716 sysarg_t cols = IPC_GET_ARG4(*icall); 717 sysarg_t rows = IPC_GET_ARG5(*icall); 718 719 for (sysarg_t y = 0; y < rows; y++) { 720 for (sysarg_t x = 0; x < cols; x++) { 721 charfield_t *front_field = 722 screenbuffer_field_at(screenbuf, col + x, row + y); 723 charfield_t *back_field = 724 screenbuffer_field_at(vp->backbuf, col + x, row + y); 725 bool update = false; 726 727 if (front_field->ch != back_field->ch) { 728 back_field->ch = front_field->ch; 729 update = true; 730 } 731 732 if (!attrs_same(front_field->attrs, back_field->attrs)) { 733 back_field->attrs = front_field->attrs; 734 update = true; 735 } 736 737 front_field->flags &= ~CHAR_FLAG_DIRTY; 738 739 if (update) 740 dev->ops.vp_char_update(dev, vp, col + x, row + y); 741 } 742 } 743 744 async_answer_0(iid, EOK); 745 } else 746 async_answer_0(iid, ENOENT); 747 } 748 749 static void fbsrv_vp_imagemap_damage(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 750 { 751 imagemap_t *imagemap = resolve_imagemap(dev, IPC_GET_ARG1(*icall), iid); 752 if (imagemap == NULL) 753 return; 754 755 if ((dev->claimed) && (dev->ops.vp_imgmap_damage)) { 756 if (dev->active_vp) { 757 dev->ops.vp_imgmap_damage(dev, dev->active_vp, 758 (imgmap_t *) imagemap->data, 759 IPC_GET_ARG2(*icall), IPC_GET_ARG3(*icall), 760 IPC_GET_ARG4(*icall), IPC_GET_ARG5(*icall)); 761 async_answer_0(iid, EOK); 762 } else 763 async_answer_0(iid, ENOENT); 764 } else 765 async_answer_0(iid, ENOTSUP); 766 } 767 768 static void fbsrv_vp_sequence_start(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 769 { 770 sequence_t *sequence = resolve_sequence(dev, IPC_GET_ARG1(*icall), iid); 771 if (sequence == NULL) 772 return; 773 774 if (dev->active_vp) { 775 /* Check if the sequence is not already started */ 776 list_foreach(dev->active_vp->sequences, link) { 777 sequence_vp_t *seq_vp = 778 list_get_instance(link, sequence_vp_t, link); 779 780 if (seq_vp->seq == sequence) { 781 async_answer_0(iid, EEXISTS); 782 return; 783 } 784 } 785 786 sequence_vp_t *seq_vp = 787 (sequence_vp_t *) malloc(sizeof(sequence_vp_t)); 788 789 if (seq_vp != NULL) { 790 link_initialize(&seq_vp->link); 791 seq_vp->seq = sequence; 792 seq_vp->current = 0; 793 794 list_append(&seq_vp->link, &dev->active_vp->sequences); 795 async_answer_0(iid, EOK); 796 } else 797 async_answer_0(iid, ENOMEM); 798 } else 799 async_answer_0(iid, ENOENT); 800 } 801 802 static void fbsrv_vp_sequence_stop(fbdev_t *dev, ipc_callid_t iid, ipc_call_t *icall) 803 { 804 sequence_t *sequence = resolve_sequence(dev, IPC_GET_ARG1(*icall), iid); 805 if (sequence == NULL) 806 return; 807 808 if (dev->active_vp) { 809 list_foreach(dev->active_vp->sequences, link) { 810 sequence_vp_t *seq_vp = 811 list_get_instance(link, sequence_vp_t, link); 812 813 if (seq_vp->seq == sequence) { 814 list_remove(&seq_vp->link); 815 free(seq_vp); 816 817 async_answer_0(iid, EOK); 818 return; 819 } 820 } 821 822 async_answer_0(iid, ENOENT); 823 } else 824 async_answer_0(iid, ENOENT); 825 } 826 827 static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) 828 { 829 fbdev_t *dev = NULL; 830 service_id_t dsid = (service_id_t) IPC_GET_ARG1(*icall); 831 832 list_foreach(fbdevs, link) { 833 fbdev_t *fbdev = list_get_instance(link, fbdev_t, link); 834 if (fbdev->dsid == dsid) { 835 dev = fbdev; 836 break; 837 } 838 } 839 840 if (dev == NULL) { 841 async_answer_0(iid, ENOENT); 842 return; 843 } 844 845 if (atomic_get(&dev->refcnt) > 0) { 846 async_answer_0(iid, ELIMIT); 847 return; 848 } 126 849 127 850 /* 128 * Style and glyphs for text printing851 * Accept the connection 129 852 */ 130 853 131 /** Current attributes. */ 132 attr_rgb_t attr; 133 134 uint8_t *bgpixel; 135 136 /** 137 * Glyph drawing function for this viewport. Different viewports 138 * might use different drawing functions depending on whether their 139 * scanlines are aligned on a word boundary. 140 */ 141 dg_t dglyph; 142 143 /* Auto-cursor position */ 144 bool cursor_active; 145 unsigned int cur_col; 146 unsigned int cur_row; 147 bool cursor_shown; 148 149 /* Back buffer */ 150 bb_cell_t *backbuf; 151 unsigned int bbsize; 152 } viewport_t; 153 154 typedef struct { 155 bool initialized; 156 bool enabled; 157 unsigned int vp; 158 159 unsigned int pos; 160 unsigned int animlen; 161 unsigned int pixmaps[MAX_ANIM_LEN]; 162 } animation_t; 163 164 static animation_t animations[MAX_ANIMATIONS]; 165 static bool anims_enabled; 166 167 typedef struct { 168 unsigned int width; 169 unsigned int height; 170 uint8_t *data; 171 } pixmap_t; 172 173 static pixmap_t pixmaps[MAX_PIXMAPS]; 174 static viewport_t viewports[128]; 175 176 static bool client_connected = false; /**< Allow only 1 connection */ 177 178 static uint32_t color_table[16] = { 179 [COLOR_BLACK] = 0x000000, 180 [COLOR_BLUE] = 0x0000f0, 181 [COLOR_GREEN] = 0x00f000, 182 [COLOR_CYAN] = 0x00f0f0, 183 [COLOR_RED] = 0xf00000, 184 [COLOR_MAGENTA] = 0xf000f0, 185 [COLOR_YELLOW] = 0xf0f000, 186 [COLOR_WHITE] = 0xf0f0f0, 187 188 [8 + COLOR_BLACK] = 0x000000, 189 [8 + COLOR_BLUE] = 0x0000ff, 190 [8 + COLOR_GREEN] = 0x00ff00, 191 [8 + COLOR_CYAN] = 0x00ffff, 192 [8 + COLOR_RED] = 0xff0000, 193 [8 + COLOR_MAGENTA] = 0xff00ff, 194 [8 + COLOR_YELLOW] = 0xffff00, 195 [8 + COLOR_WHITE] = 0xffffff, 196 }; 197 198 static int rgb_from_attr(attr_rgb_t *rgb, const attrs_t *a); 199 static int rgb_from_style(attr_rgb_t *rgb, int style); 200 static int rgb_from_idx(attr_rgb_t *rgb, sysarg_t fg_color, 201 sysarg_t bg_color, sysarg_t flags); 202 203 static int fb_set_color(viewport_t *vport, sysarg_t fg_color, 204 sysarg_t bg_color, sysarg_t attr); 205 206 static void draw_glyph_aligned(unsigned int x, unsigned int y, bool cursor, 207 uint8_t *glyphs, uint32_t glyph, uint32_t fg_color, uint32_t bg_color); 208 static void draw_glyph_fallback(unsigned int x, unsigned int y, bool cursor, 209 uint8_t *glyphs, uint32_t glyph, uint32_t fg_color, uint32_t bg_color); 210 211 static void draw_vp_glyph(viewport_t *vport, bool cursor, unsigned int col, 212 unsigned int row); 213 214 215 #define RED(x, bits) (((x) >> (8 + 8 + 8 - (bits))) & ((1 << (bits)) - 1)) 216 #define GREEN(x, bits) (((x) >> (8 + 8 - (bits))) & ((1 << (bits)) - 1)) 217 #define BLUE(x, bits) (((x) >> (8 - (bits))) & ((1 << (bits)) - 1)) 218 219 #define COL2X(col) ((col) * FONT_WIDTH) 220 #define ROW2Y(row) ((row) * FONT_SCANLINES) 221 222 #define X2COL(x) ((x) / FONT_WIDTH) 223 #define Y2ROW(y) ((y) / FONT_SCANLINES) 224 225 #define FB_POS(x, y) ((y) * screen.scanline + (x) * screen.pixelbytes) 226 #define BB_POS(vport, col, row) ((row) * vport->cols + (col)) 227 #define GLYPH_POS(glyph, y, cursor) (((glyph) + (cursor) * FONT_GLYPHS) * screen.glyphbytes + (y) * screen.glyphscanline) 228 229 /* 230 * RGB conversion and mask functions. 231 * 232 * These functions write an RGB value to some memory in some predefined format. 233 * The naming convention corresponds to the format created by these functions. 234 * The functions use the so called network order (i.e. big endian) with respect 235 * to their names. 236 */ 237 238 static void rgb_0888(void *dst, uint32_t rgb) 239 { 240 *((uint32_t *) dst) = host2uint32_t_be((0 << 24) | 241 (RED(rgb, 8) << 16) | (GREEN(rgb, 8) << 8) | (BLUE(rgb, 8))); 242 } 243 244 static void bgr_0888(void *dst, uint32_t rgb) 245 { 246 *((uint32_t *) dst) = host2uint32_t_be((0 << 24) | 247 (BLUE(rgb, 8) << 16) | (GREEN(rgb, 8) << 8) | (RED(rgb, 8))); 248 } 249 250 static void mask_0888(void *dst, bool mask) 251 { 252 bgr_0888(dst, mask ? 0xffffff : 0); 253 } 254 255 static void rgb_8880(void *dst, uint32_t rgb) 256 { 257 *((uint32_t *) dst) = host2uint32_t_be((RED(rgb, 8) << 24) | 258 (GREEN(rgb, 8) << 16) | (BLUE(rgb, 8) << 8) | 0); 259 } 260 261 static void bgr_8880(void *dst, uint32_t rgb) 262 { 263 *((uint32_t *) dst) = host2uint32_t_be((BLUE(rgb, 8) << 24) | 264 (GREEN(rgb, 8) << 16) | (RED(rgb, 8) << 8) | 0); 265 } 266 267 static void mask_8880(void *dst, bool mask) 268 { 269 bgr_8880(dst, mask ? 0xffffff : 0); 270 } 271 272 static void rgb_888(void *dst, uint32_t rgb) 273 { 274 ((uint8_t *) dst)[0] = RED(rgb, 8); 275 ((uint8_t *) dst)[1] = GREEN(rgb, 8); 276 ((uint8_t *) dst)[2] = BLUE(rgb, 8); 277 } 278 279 static void bgr_888(void *dst, uint32_t rgb) 280 { 281 ((uint8_t *) dst)[0] = BLUE(rgb, 8); 282 ((uint8_t *) dst)[1] = GREEN(rgb, 8); 283 ((uint8_t *) dst)[2] = RED(rgb, 8); 284 } 285 286 static void mask_888(void *dst, bool mask) 287 { 288 bgr_888(dst, mask ? 0xffffff : 0); 289 } 290 291 static void rgb_555_be(void *dst, uint32_t rgb) 292 { 293 *((uint16_t *) dst) = host2uint16_t_be(RED(rgb, 5) << 10 | 294 GREEN(rgb, 5) << 5 | BLUE(rgb, 5)); 295 } 296 297 static void rgb_555_le(void *dst, uint32_t rgb) 298 { 299 *((uint16_t *) dst) = host2uint16_t_le(RED(rgb, 5) << 10 | 300 GREEN(rgb, 5) << 5 | BLUE(rgb, 5)); 301 } 302 303 static void rgb_565_be(void *dst, uint32_t rgb) 304 { 305 *((uint16_t *) dst) = host2uint16_t_be(RED(rgb, 5) << 11 | 306 GREEN(rgb, 6) << 5 | BLUE(rgb, 5)); 307 } 308 309 static void rgb_565_le(void *dst, uint32_t rgb) 310 { 311 *((uint16_t *) dst) = host2uint16_t_le(RED(rgb, 5) << 11 | 312 GREEN(rgb, 6) << 5 | BLUE(rgb, 5)); 313 } 314 315 static void mask_555(void *dst, bool mask) 316 { 317 rgb_555_be(dst, mask ? 0xffffff : 0); 318 } 319 320 static void mask_565(void *dst, bool mask) 321 { 322 rgb_565_be(dst, mask ? 0xffffff : 0); 323 } 324 325 static void bgr_323(void *dst, uint32_t rgb) 326 { 327 *((uint8_t *) dst) 328 = ~((RED(rgb, 3) << 5) | (GREEN(rgb, 2) << 3) | BLUE(rgb, 3)); 329 } 330 331 static void mask_323(void *dst, bool mask) 332 { 333 bgr_323(dst, mask ? 0x0 : ~0x0); 334 } 335 336 /** Draw a filled rectangle. 337 * 338 * @note Need real implementation that does not access VRAM twice. 339 * 340 */ 341 static void draw_filled_rect(unsigned int x0, unsigned int y0, unsigned int x1, 342 unsigned int y1, uint32_t color) 343 { 344 unsigned int x; 345 unsigned int y; 346 unsigned int copy_bytes; 347 348 uint8_t *sp; 349 uint8_t *dp; 350 uint8_t cbuf[4]; 351 352 if ((y0 >= y1) || (x0 >= x1)) 353 return; 354 355 screen.rgb_conv(cbuf, color); 356 357 sp = &screen.fb_addr[FB_POS(x0, y0)]; 358 dp = sp; 359 360 /* Draw the first line. */ 361 for (x = x0; x < x1; x++) { 362 memcpy(dp, cbuf, screen.pixelbytes); 363 dp += screen.pixelbytes; 364 } 365 366 dp = sp + screen.scanline; 367 copy_bytes = (x1 - x0) * screen.pixelbytes; 368 369 /* Draw the remaining lines by copying. */ 370 for (y = y0 + 1; y < y1; y++) { 371 memcpy(dp, sp, copy_bytes); 372 dp += screen.scanline; 373 } 374 } 375 376 /** Redraw viewport. 377 * 378 * @param vport Viewport to redraw 379 * 380 */ 381 static void vport_redraw(viewport_t *vport) 382 { 383 unsigned int col; 384 unsigned int row; 385 386 for (row = 0; row < vport->rows; row++) { 387 for (col = 0; col < vport->cols; col++) { 388 draw_vp_glyph(vport, false, col, row); 389 } 390 } 391 392 if (COL2X(vport->cols) < vport->width) { 393 draw_filled_rect( 394 vport->x + COL2X(vport->cols), vport->y, 395 vport->x + vport->width, vport->y + vport->height, 396 vport->attr.bg_color); 397 } 398 399 if (ROW2Y(vport->rows) < vport->height) { 400 draw_filled_rect( 401 vport->x, vport->y + ROW2Y(vport->rows), 402 vport->x + vport->width, vport->y + vport->height, 403 vport->attr.bg_color); 404 } 405 } 406 407 static void backbuf_clear(bb_cell_t *backbuf, size_t len, uint32_t fg_color, 408 uint32_t bg_color) 409 { 410 size_t i; 411 412 for (i = 0; i < len; i++) { 413 backbuf[i].glyph = 0; 414 backbuf[i].fg_color = fg_color; 415 backbuf[i].bg_color = bg_color; 416 } 417 } 418 419 /** Clear viewport. 420 * 421 * @param vport Viewport to clear 422 * 423 */ 424 static void vport_clear(viewport_t *vport) 425 { 426 backbuf_clear(vport->backbuf, vport->cols * vport->rows, 427 vport->attr.fg_color, vport->attr.bg_color); 428 vport_redraw(vport); 429 } 430 431 /** Scroll viewport by the specified number of lines. 432 * 433 * @param vport Viewport to scroll 434 * @param lines Number of lines to scroll 435 * 436 */ 437 static void vport_scroll(viewport_t *vport, int lines) 438 { 439 unsigned int col; 440 unsigned int row; 441 unsigned int x; 442 unsigned int y; 443 uint32_t glyph; 444 uint32_t fg_color; 445 uint32_t bg_color; 446 bb_cell_t *bbp; 447 bb_cell_t *xbp; 448 449 /* 450 * Redraw. 451 */ 452 453 y = vport->y; 454 for (row = 0; row < vport->rows; row++) { 455 x = vport->x; 456 for (col = 0; col < vport->cols; col++) { 457 if (((int) row + lines >= 0) && 458 ((int) row + lines < (int) vport->rows)) { 459 xbp = &vport->backbuf[BB_POS(vport, col, row + lines)]; 460 bbp = &vport->backbuf[BB_POS(vport, col, row)]; 461 462 glyph = xbp->glyph; 463 fg_color = xbp->fg_color; 464 bg_color = xbp->bg_color; 465 466 if ((bbp->glyph == glyph) 467 && (bbp->fg_color == xbp->fg_color) 468 && (bbp->bg_color == xbp->bg_color)) { 469 x += FONT_WIDTH; 470 continue; 471 } 472 } else { 473 glyph = 0; 474 fg_color = vport->attr.fg_color; 475 bg_color = vport->attr.bg_color; 476 } 477 478 (*vport->dglyph)(x, y, false, screen.glyphs, glyph, 479 fg_color, bg_color); 480 x += FONT_WIDTH; 481 } 482 y += FONT_SCANLINES; 483 } 484 485 /* 486 * Scroll backbuffer. 487 */ 488 489 if (lines > 0) { 490 memmove(vport->backbuf, vport->backbuf + vport->cols * lines, 491 vport->cols * (vport->rows - lines) * sizeof(bb_cell_t)); 492 backbuf_clear(&vport->backbuf[BB_POS(vport, 0, vport->rows - lines)], 493 vport->cols * lines, vport->attr.fg_color, vport->attr.bg_color); 494 } else { 495 memmove(vport->backbuf - vport->cols * lines, vport->backbuf, 496 vport->cols * (vport->rows + lines) * sizeof(bb_cell_t)); 497 backbuf_clear(vport->backbuf, - vport->cols * lines, 498 vport->attr.fg_color, vport->attr.bg_color); 499 } 500 } 501 502 /** Render glyphs 503 * 504 * Convert glyphs from device independent font 505 * description to current visual representation. 506 * 507 */ 508 static void render_glyphs(void) 509 { 510 unsigned int glyph; 511 512 for (glyph = 0; glyph < FONT_GLYPHS; glyph++) { 513 unsigned int y; 514 515 for (y = 0; y < FONT_SCANLINES; y++) { 516 unsigned int x; 517 518 for (x = 0; x < FONT_WIDTH; x++) { 519 screen.mask_conv(&screen.glyphs[GLYPH_POS(glyph, y, false) + x * screen.pixelbytes], 520 (fb_font[glyph][y] & (1 << (7 - x))) ? true : false); 521 522 screen.mask_conv(&screen.glyphs[GLYPH_POS(glyph, y, true) + x * screen.pixelbytes], 523 (fb_font[glyph][y] & (1 << (7 - x))) ? false : true); 524 } 525 } 526 } 527 } 528 529 /** Create new viewport 530 * 531 * @param x Origin of the viewport (x). 532 * @param y Origin of the viewport (y). 533 * @param width Width of the viewport. 534 * @param height Height of the viewport. 535 * 536 * @return New viewport number. 537 * 538 */ 539 static int vport_create(unsigned int x, unsigned int y, 540 unsigned int width, unsigned int height) 541 { 542 unsigned int i; 543 544 for (i = 0; i < MAX_VIEWPORTS; i++) { 545 if (!viewports[i].initialized) 854 atomic_inc(&dev->refcnt); 855 dev->claimed = true; 856 async_answer_0(iid, EOK); 857 858 while (true) { 859 ipc_call_t call; 860 ipc_callid_t callid = 861 async_get_call_timeout(&call, TICK_INTERVAL); 862 863 if (!callid) { 864 fbsrv_vp_cursor_flash(dev); 865 fbsrv_sequences_update(dev); 866 continue; 867 } 868 869 if (!IPC_GET_IMETHOD(call)) { 870 dev->claimed = false; 871 atomic_dec(&dev->refcnt); 872 async_answer_0(callid, EOK); 546 873 break; 547 } 548 549 if (i == MAX_VIEWPORTS) 550 return ELIMIT; 551 552 unsigned int cols = width / FONT_WIDTH; 553 unsigned int rows = height / FONT_SCANLINES; 554 unsigned int bbsize = cols * rows * sizeof(bb_cell_t); 555 unsigned int word_size = sizeof(unsigned long); 556 557 bb_cell_t *backbuf = (bb_cell_t *) malloc(bbsize); 558 if (!backbuf) 559 return ENOMEM; 560 561 uint8_t *bgpixel = (uint8_t *) malloc(screen.pixelbytes); 562 if (!bgpixel) { 563 free(backbuf); 564 return ENOMEM; 565 } 566 567 backbuf_clear(backbuf, cols * rows, DEFAULT_FGCOLOR, DEFAULT_BGCOLOR); 568 memset(bgpixel, 0, screen.pixelbytes); 569 570 viewports[i].x = x; 571 viewports[i].y = y; 572 viewports[i].width = width; 573 viewports[i].height = height; 574 575 viewports[i].cols = cols; 576 viewports[i].rows = rows; 577 578 viewports[i].attr.bg_color = DEFAULT_BGCOLOR; 579 viewports[i].attr.fg_color = DEFAULT_FGCOLOR; 580 581 viewports[i].bgpixel = bgpixel; 582 583 /* 584 * Conditions necessary to select aligned version: 585 * - word size is divisible by pixelbytes 586 * - cell scanline size is divisible by word size 587 * - cell scanlines are word-aligned 588 * 589 */ 590 if (((word_size % screen.pixelbytes) == 0) 591 && ((FONT_WIDTH * screen.pixelbytes) % word_size == 0) 592 && ((x * screen.pixelbytes) % word_size == 0) 593 && (screen.scanline % word_size == 0)) { 594 viewports[i].dglyph = draw_glyph_aligned; 595 } else { 596 viewports[i].dglyph = draw_glyph_fallback; 597 } 598 599 viewports[i].cur_col = 0; 600 viewports[i].cur_row = 0; 601 viewports[i].cursor_active = false; 602 viewports[i].cursor_shown = false; 603 604 viewports[i].bbsize = bbsize; 605 viewports[i].backbuf = backbuf; 606 607 viewports[i].initialized = true; 608 609 screen.rgb_conv(viewports[i].bgpixel, viewports[i].attr.bg_color); 610 611 return i; 612 } 613 614 615 /** Initialize framebuffer as a chardev output device 616 * 617 * @param addr Address of the framebuffer 618 * @param xres Screen width in pixels 619 * @param yres Screen height in pixels 620 * @param visual Bits per pixel (8, 16, 24, 32) 621 * @param scan Bytes per one scanline 622 * 623 */ 624 static bool screen_init(void *addr, unsigned int xres, unsigned int yres, 625 unsigned int scan, unsigned int visual) 626 { 627 switch (visual) { 628 case VISUAL_INDIRECT_8: 629 screen.rgb_conv = bgr_323; 630 screen.mask_conv = mask_323; 631 screen.pixelbytes = 1; 632 break; 633 case VISUAL_RGB_5_5_5_LE: 634 screen.rgb_conv = rgb_555_le; 635 screen.mask_conv = mask_555; 636 screen.pixelbytes = 2; 637 break; 638 case VISUAL_RGB_5_5_5_BE: 639 screen.rgb_conv = rgb_555_be; 640 screen.mask_conv = mask_555; 641 screen.pixelbytes = 2; 642 break; 643 case VISUAL_RGB_5_6_5_LE: 644 screen.rgb_conv = rgb_565_le; 645 screen.mask_conv = mask_565; 646 screen.pixelbytes = 2; 647 break; 648 case VISUAL_RGB_5_6_5_BE: 649 screen.rgb_conv = rgb_565_be; 650 screen.mask_conv = mask_565; 651 screen.pixelbytes = 2; 652 break; 653 case VISUAL_RGB_8_8_8: 654 screen.rgb_conv = rgb_888; 655 screen.mask_conv = mask_888; 656 screen.pixelbytes = 3; 657 break; 658 case VISUAL_BGR_8_8_8: 659 screen.rgb_conv = bgr_888; 660 screen.mask_conv = mask_888; 661 screen.pixelbytes = 3; 662 break; 663 case VISUAL_RGB_8_8_8_0: 664 screen.rgb_conv = rgb_8880; 665 screen.mask_conv = mask_8880; 666 screen.pixelbytes = 4; 667 break; 668 case VISUAL_RGB_0_8_8_8: 669 screen.rgb_conv = rgb_0888; 670 screen.mask_conv = mask_0888; 671 screen.pixelbytes = 4; 672 break; 673 case VISUAL_BGR_0_8_8_8: 674 screen.rgb_conv = bgr_0888; 675 screen.mask_conv = mask_0888; 676 screen.pixelbytes = 4; 677 break; 678 case VISUAL_BGR_8_8_8_0: 679 screen.rgb_conv = bgr_8880; 680 screen.mask_conv = mask_8880; 681 screen.pixelbytes = 4; 682 break; 683 default: 684 return false; 685 } 686 687 screen.fb_addr = (unsigned char *) addr; 688 screen.xres = xres; 689 screen.yres = yres; 690 screen.scanline = scan; 691 692 screen.glyphscanline = FONT_WIDTH * screen.pixelbytes; 693 screen.glyphbytes = screen.glyphscanline * FONT_SCANLINES; 694 695 size_t glyphsize = 2 * FONT_GLYPHS * screen.glyphbytes; 696 uint8_t *glyphs = (uint8_t *) malloc(glyphsize); 697 if (!glyphs) 698 return false; 699 700 memset(glyphs, 0, glyphsize); 701 screen.glyphs = glyphs; 702 703 render_glyphs(); 704 705 /* Create first viewport */ 706 vport_create(0, 0, xres, yres); 707 708 return true; 709 } 710 711 712 /** Draw a glyph, takes advantage of alignment. 713 * 714 * This version can only be used if the following conditions are met: 715 * 716 * - word size is divisible by pixelbytes 717 * - cell scanline size is divisible by word size 718 * - cell scanlines are word-aligned 719 * 720 * It makes use of the pre-rendered mask to process (possibly) several 721 * pixels at once (word size / pixelbytes pixels at a time are processed) 722 * making it very fast. Most notably this version is not applicable at 24 bits 723 * per pixel. 724 * 725 * @param x x coordinate of top-left corner on screen. 726 * @param y y coordinate of top-left corner on screen. 727 * @param cursor Draw glyph with cursor 728 * @param glyphs Pointer to font bitmap. 729 * @param glyph Code of the glyph to draw. 730 * @param fg_color Foreground color. 731 * @param bg_color Backgroudn color. 732 * 733 */ 734 static void draw_glyph_aligned(unsigned int x, unsigned int y, bool cursor, 735 uint8_t *glyphs, uint32_t glyph, uint32_t fg_color, uint32_t bg_color) 736 { 737 unsigned int i; 738 unsigned int yd; 739 unsigned long fg_buf; 740 unsigned long bg_buf; 741 unsigned long mask; 742 743 /* 744 * Prepare a pair of words, one filled with foreground-color 745 * pattern and the other filled with background-color pattern. 746 */ 747 for (i = 0; i < sizeof(unsigned long) / screen.pixelbytes; i++) { 748 screen.rgb_conv(&((uint8_t *) &fg_buf)[i * screen.pixelbytes], 749 fg_color); 750 screen.rgb_conv(&((uint8_t *) &bg_buf)[i * screen.pixelbytes], 751 bg_color); 752 } 753 754 /* Pointer to the current position in the mask. */ 755 unsigned long *maskp = (unsigned long *) &glyphs[GLYPH_POS(glyph, 0, cursor)]; 756 757 /* Pointer to the current position on the screen. */ 758 unsigned long *dp = (unsigned long *) &screen.fb_addr[FB_POS(x, y)]; 759 760 /* Width of the character cell in words. */ 761 unsigned int ww = FONT_WIDTH * screen.pixelbytes / sizeof(unsigned long); 762 763 /* Offset to add when moving to another screen scanline. */ 764 unsigned int d_add = screen.scanline - FONT_WIDTH * screen.pixelbytes; 765 766 for (yd = 0; yd < FONT_SCANLINES; yd++) { 767 /* 768 * Now process the cell scanline, combining foreground 769 * and background color patters using the pre-rendered mask. 770 */ 771 for (i = 0; i < ww; i++) { 772 mask = *maskp++; 773 *dp++ = (fg_buf & mask) | (bg_buf & ~mask); 774 } 775 776 /* Move to the beginning of the next scanline of the cell. */ 777 dp = (unsigned long *) ((uint8_t *) dp + d_add); 778 } 779 } 780 781 /** Draw a glyph, fallback version. 782 * 783 * This version does not make use of the pre-rendered mask, it uses 784 * the font bitmap directly. It works always, but it is slower. 785 * 786 * @param x x coordinate of top-left corner on screen. 787 * @param y y coordinate of top-left corner on screen. 788 * @param cursor Draw glyph with cursor 789 * @param glyphs Pointer to font bitmap. 790 * @param glyph Code of the glyph to draw. 791 * @param fg_color Foreground color. 792 * @param bg_color Backgroudn color. 793 * 794 */ 795 void draw_glyph_fallback(unsigned int x, unsigned int y, bool cursor, 796 uint8_t *glyphs, uint32_t glyph, uint32_t fg_color, uint32_t bg_color) 797 { 798 unsigned int i; 799 unsigned int j; 800 unsigned int yd; 801 uint8_t fg_buf[4]; 802 uint8_t bg_buf[4]; 803 uint8_t *sp; 804 uint8_t b; 805 806 /* Pre-render 1x the foreground and background color pixels. */ 807 if (cursor) { 808 screen.rgb_conv(fg_buf, bg_color); 809 screen.rgb_conv(bg_buf, fg_color); 810 } else { 811 screen.rgb_conv(fg_buf, fg_color); 812 screen.rgb_conv(bg_buf, bg_color); 813 } 814 815 /* Pointer to the current position on the screen. */ 816 uint8_t *dp = (uint8_t *) &screen.fb_addr[FB_POS(x, y)]; 817 818 /* Offset to add when moving to another screen scanline. */ 819 unsigned int d_add = screen.scanline - FONT_WIDTH * screen.pixelbytes; 820 821 for (yd = 0; yd < FONT_SCANLINES; yd++) { 822 /* Byte containing bits of the glyph scanline. */ 823 b = fb_font[glyph][yd]; 824 825 for (i = 0; i < FONT_WIDTH; i++) { 826 /* Choose color based on the current bit. */ 827 sp = (b & 0x80) ? fg_buf : bg_buf; 828 829 /* Copy the pixel. */ 830 for (j = 0; j < screen.pixelbytes; j++) { 831 *dp++ = *sp++; 832 } 833 834 /* Move to the next bit. */ 835 b = b << 1; 836 } 837 838 /* Move to the beginning of the next scanline of the cell. */ 839 dp += d_add; 840 } 841 } 842 843 /** Draw glyph at specified position in viewport. 844 * 845 * @param vport Viewport identification 846 * @param cursor Draw glyph with cursor 847 * @param col Screen position relative to viewport 848 * @param row Screen position relative to viewport 849 * 850 */ 851 static void draw_vp_glyph(viewport_t *vport, bool cursor, unsigned int col, 852 unsigned int row) 853 { 854 unsigned int x = vport->x + COL2X(col); 855 unsigned int y = vport->y + ROW2Y(row); 856 857 uint32_t glyph = vport->backbuf[BB_POS(vport, col, row)].glyph; 858 uint32_t fg_color = vport->backbuf[BB_POS(vport, col, row)].fg_color; 859 uint32_t bg_color = vport->backbuf[BB_POS(vport, col, row)].bg_color; 860 861 (*vport->dglyph)(x, y, cursor, screen.glyphs, glyph, 862 fg_color, bg_color); 863 } 864 865 /** Hide cursor if it is shown 866 * 867 */ 868 static void cursor_hide(viewport_t *vport) 869 { 870 if ((vport->cursor_active) && (vport->cursor_shown)) { 871 draw_vp_glyph(vport, false, vport->cur_col, vport->cur_row); 872 vport->cursor_shown = false; 873 } 874 } 875 876 877 /** Show cursor if cursor showing is enabled 878 * 879 */ 880 static void cursor_show(viewport_t *vport) 881 { 882 /* Do not check for cursor_shown */ 883 if (vport->cursor_active) { 884 draw_vp_glyph(vport, true, vport->cur_col, vport->cur_row); 885 vport->cursor_shown = true; 886 } 887 } 888 889 890 /** Invert cursor, if it is enabled 891 * 892 */ 893 static void cursor_blink(viewport_t *vport) 894 { 895 if (vport->cursor_shown) 896 cursor_hide(vport); 897 else 898 cursor_show(vport); 899 } 900 901 902 /** Draw character at given position relative to viewport 903 * 904 * @param vport Viewport identification 905 * @param c Character to draw 906 * @param col Screen position relative to viewport 907 * @param row Screen position relative to viewport 908 * 909 */ 910 static void draw_char(viewport_t *vport, wchar_t c, unsigned int col, unsigned int row) 911 { 912 bb_cell_t *bbp; 913 914 /* Do not hide cursor if we are going to overwrite it */ 915 if ((vport->cursor_active) && (vport->cursor_shown) && 916 ((vport->cur_col != col) || (vport->cur_row != row))) 917 cursor_hide(vport); 918 919 bbp = &vport->backbuf[BB_POS(vport, col, row)]; 920 bbp->glyph = fb_font_glyph(c); 921 bbp->fg_color = vport->attr.fg_color; 922 bbp->bg_color = vport->attr.bg_color; 923 924 draw_vp_glyph(vport, false, col, row); 925 926 vport->cur_col = col; 927 vport->cur_row = row; 928 929 vport->cur_col++; 930 if (vport->cur_col >= vport->cols) { 931 vport->cur_col = 0; 932 vport->cur_row++; 933 if (vport->cur_row >= vport->rows) 934 vport->cur_row--; 935 } 936 937 cursor_show(vport); 938 } 939 940 /** Draw text data to viewport. 941 * 942 * @param vport Viewport id 943 * @param data Text data. 944 * @param x Leftmost column of the area. 945 * @param y Topmost row of the area. 946 * @param w Number of rows. 947 * @param h Number of columns. 948 * 949 */ 950 static void draw_text_data(viewport_t *vport, keyfield_t *data, unsigned int x, 951 unsigned int y, unsigned int w, unsigned int h) 952 { 953 unsigned int i; 954 unsigned int j; 955 bb_cell_t *bbp; 956 attrs_t *a; 957 958 for (j = 0; j < h; j++) { 959 for (i = 0; i < w; i++) { 960 unsigned int col = x + i; 961 unsigned int row = y + j; 962 963 bbp = &vport->backbuf[BB_POS(vport, col, row)]; 964 965 a = &data[j * w + i].attrs; 966 967 attr_rgb_t rgb; 968 rgb.fg_color = 0; 969 rgb.bg_color = 0; 970 rgb_from_attr(&rgb, a); 971 972 bbp->glyph = fb_font_glyph(data[j * w + i].character); 973 bbp->fg_color = rgb.fg_color; 974 bbp->bg_color = rgb.bg_color; 975 976 draw_vp_glyph(vport, false, col, row); 977 } 978 } 979 cursor_show(vport); 980 } 981 982 983 static void putpixel_pixmap(void *data, unsigned int x, unsigned int y, uint32_t color) 984 { 985 int pm = *((int *) data); 986 pixmap_t *pmap = &pixmaps[pm]; 987 unsigned int pos = (y * pmap->width + x) * screen.pixelbytes; 988 989 screen.rgb_conv(&pmap->data[pos], color); 990 } 991 992 993 static void putpixel(void *data, unsigned int x, unsigned int y, uint32_t color) 994 { 995 viewport_t *vport = (viewport_t *) data; 996 unsigned int dx = vport->x + x; 997 unsigned int dy = vport->y + y; 998 999 screen.rgb_conv(&screen.fb_addr[FB_POS(dx, dy)], color); 1000 } 1001 1002 1003 /** Return first free pixmap 1004 * 1005 */ 1006 static int find_free_pixmap(void) 1007 { 1008 unsigned int i; 1009 1010 for (i = 0; i < MAX_PIXMAPS; i++) 1011 if (!pixmaps[i].data) 1012 return i; 1013 1014 return -1; 1015 } 1016 1017 1018 /** Create a new pixmap and return appropriate ID 1019 * 1020 */ 1021 static int shm2pixmap(unsigned char *shm, size_t size) 1022 { 1023 int pm; 1024 pixmap_t *pmap; 1025 1026 pm = find_free_pixmap(); 1027 if (pm == -1) 1028 return ELIMIT; 1029 1030 pmap = &pixmaps[pm]; 1031 1032 if (ppm_get_data(shm, size, &pmap->width, &pmap->height)) 1033 return EINVAL; 1034 1035 pmap->data = malloc(pmap->width * pmap->height * screen.pixelbytes); 1036 if (!pmap->data) 1037 return ENOMEM; 1038 1039 ppm_draw(shm, size, 0, 0, pmap->width, pmap->height, putpixel_pixmap, (void *) &pm); 1040 1041 return pm; 1042 } 1043 1044 1045 /** Handle shared memory communication calls 1046 * 1047 * Protocol for drawing pixmaps: 1048 * - FB_PREPARE_SHM(client shm identification) 1049 * - IPC_M_AS_AREA_SEND 1050 * - FB_DRAW_PPM(startx, starty) 1051 * - FB_DROP_SHM 1052 * 1053 * Protocol for text drawing 1054 * - IPC_M_AS_AREA_SEND 1055 * - FB_DRAW_TEXT_DATA 1056 * 1057 * @param callid Callid of the current call 1058 * @param call Current call data 1059 * @param vp Active viewport 1060 * 1061 * @return false if the call was not handled byt this function, true otherwise 1062 * 1063 * Note: this function is not thread-safe, you would have 1064 * to redefine static variables with fibril_local. 1065 * 1066 */ 1067 static bool shm_handle(ipc_callid_t callid, ipc_call_t *call, int vp) 1068 { 1069 static keyfield_t *interbuffer = NULL; 1070 static size_t intersize = 0; 1071 1072 static unsigned char *shm = NULL; 1073 static sysarg_t shm_id = 0; 1074 static size_t shm_size; 1075 1076 bool handled = true; 1077 int retval = EOK; 1078 viewport_t *vport = &viewports[vp]; 1079 unsigned int x; 1080 unsigned int y; 1081 unsigned int w; 1082 unsigned int h; 1083 1084 switch (IPC_GET_IMETHOD(*call)) { 1085 case IPC_M_SHARE_OUT: 1086 /* We accept one area for data interchange */ 1087 if (IPC_GET_ARG1(*call) == shm_id) { 1088 void *dest = as_get_mappable_page(IPC_GET_ARG2(*call)); 1089 shm_size = IPC_GET_ARG2(*call); 1090 if (async_answer_1(callid, EOK, (sysarg_t) dest)) { 1091 shm_id = 0; 1092 return false; 1093 } 1094 shm = dest; 1095 1096 if (shm[0] != 'P') 1097 return false; 1098 1099 return true; 1100 } else { 1101 intersize = IPC_GET_ARG2(*call); 1102 receive_comm_area(callid, call, (void *) &interbuffer); 1103 } 1104 return true; 1105 case FB_PREPARE_SHM: 1106 if (shm_id) 1107 retval = EBUSY; 1108 else 1109 shm_id = IPC_GET_ARG1(*call); 1110 break; 1111 1112 case FB_DROP_SHM: 1113 if (shm) { 1114 as_area_destroy(shm); 1115 shm = NULL; 1116 } 1117 shm_id = 0; 1118 break; 1119 1120 case FB_SHM2PIXMAP: 1121 if (!shm) { 1122 retval = EINVAL; 1123 break; 1124 } 1125 retval = shm2pixmap(shm, shm_size); 1126 break; 1127 case FB_DRAW_PPM: 1128 if (!shm) { 1129 retval = EINVAL; 1130 break; 1131 } 1132 x = IPC_GET_ARG1(*call); 1133 y = IPC_GET_ARG2(*call); 1134 1135 if ((x > vport->width) || (y > vport->height)) { 1136 retval = EINVAL; 1137 break; 1138 } 1139 1140 ppm_draw(shm, shm_size, IPC_GET_ARG1(*call), 1141 IPC_GET_ARG2(*call), vport->width - x, vport->height - y, putpixel, (void *) vport); 1142 break; 1143 case FB_DRAW_TEXT_DATA: 1144 x = IPC_GET_ARG1(*call); 1145 y = IPC_GET_ARG2(*call); 1146 w = IPC_GET_ARG3(*call); 1147 h = IPC_GET_ARG4(*call); 1148 if (!interbuffer) { 1149 retval = EINVAL; 1150 break; 1151 } 1152 if (x + w > vport->cols || y + h > vport->rows) { 1153 retval = EINVAL; 1154 break; 1155 } 1156 if (intersize < w * h * sizeof(*interbuffer)) { 1157 retval = EINVAL; 1158 break; 1159 } 1160 draw_text_data(vport, interbuffer, x, y, w, h); 1161 break; 1162 default: 1163 handled = false; 1164 } 1165 1166 if (handled) 1167 async_answer_0(callid, retval); 1168 return handled; 1169 } 1170 1171 1172 static void copy_vp_to_pixmap(viewport_t *vport, pixmap_t *pmap) 1173 { 1174 unsigned int width = vport->width; 1175 unsigned int height = vport->height; 1176 1177 if (width + vport->x > screen.xres) 1178 width = screen.xres - vport->x; 1179 if (height + vport->y > screen.yres) 1180 height = screen.yres - vport->y; 1181 1182 unsigned int realwidth = pmap->width <= width ? pmap->width : width; 1183 unsigned int realheight = pmap->height <= height ? pmap->height : height; 1184 1185 unsigned int srcrowsize = vport->width * screen.pixelbytes; 1186 unsigned int realrowsize = realwidth * screen.pixelbytes; 1187 1188 unsigned int y; 1189 for (y = 0; y < realheight; y++) { 1190 unsigned int tmp = (vport->y + y) * screen.scanline + vport->x * screen.pixelbytes; 1191 memcpy(pmap->data + srcrowsize * y, screen.fb_addr + tmp, realrowsize); 1192 } 1193 } 1194 1195 1196 /** Save viewport to pixmap 1197 * 1198 */ 1199 static int save_vp_to_pixmap(viewport_t *vport) 1200 { 1201 int pm; 1202 pixmap_t *pmap; 1203 1204 pm = find_free_pixmap(); 1205 if (pm == -1) 1206 return ELIMIT; 1207 1208 pmap = &pixmaps[pm]; 1209 pmap->data = malloc(screen.pixelbytes * vport->width * vport->height); 1210 if (!pmap->data) 1211 return ENOMEM; 1212 1213 pmap->width = vport->width; 1214 pmap->height = vport->height; 1215 1216 copy_vp_to_pixmap(vport, pmap); 1217 1218 return pm; 1219 } 1220 1221 1222 /** Draw pixmap on screen 1223 * 1224 * @param vp Viewport to draw on 1225 * @param pm Pixmap identifier 1226 * 1227 */ 1228 static int draw_pixmap(int vp, int pm) 1229 { 1230 pixmap_t *pmap = &pixmaps[pm]; 1231 viewport_t *vport = &viewports[vp]; 1232 1233 unsigned int width = vport->width; 1234 unsigned int height = vport->height; 1235 1236 if (width + vport->x > screen.xres) 1237 width = screen.xres - vport->x; 1238 if (height + vport->y > screen.yres) 1239 height = screen.yres - vport->y; 1240 1241 if (!pmap->data) 1242 return EINVAL; 1243 1244 unsigned int realwidth = pmap->width <= width ? pmap->width : width; 1245 unsigned int realheight = pmap->height <= height ? pmap->height : height; 1246 1247 unsigned int srcrowsize = vport->width * screen.pixelbytes; 1248 unsigned int realrowsize = realwidth * screen.pixelbytes; 1249 1250 unsigned int y; 1251 for (y = 0; y < realheight; y++) { 1252 unsigned int tmp = (vport->y + y) * screen.scanline + vport->x * screen.pixelbytes; 1253 memcpy(screen.fb_addr + tmp, pmap->data + y * srcrowsize, realrowsize); 1254 } 1255 1256 return EOK; 1257 } 1258 1259 1260 /** Tick animation one step forward 1261 * 1262 */ 1263 static void anims_tick(void) 1264 { 1265 unsigned int i; 1266 static int counts = 0; 1267 1268 /* Limit redrawing */ 1269 counts = (counts + 1) % 8; 1270 if (counts) 1271 return; 1272 1273 for (i = 0; i < MAX_ANIMATIONS; i++) { 1274 if ((!animations[i].animlen) || (!animations[i].initialized) || 1275 (!animations[i].enabled)) 1276 continue; 1277 1278 draw_pixmap(animations[i].vp, animations[i].pixmaps[animations[i].pos]); 1279 animations[i].pos = (animations[i].pos + 1) % animations[i].animlen; 1280 } 1281 } 1282 1283 1284 static unsigned int pointer_x; 1285 static unsigned int pointer_y; 1286 static bool pointer_shown, pointer_enabled; 1287 static int pointer_vport = -1; 1288 static int pointer_pixmap = -1; 1289 1290 1291 static void mouse_show(void) 1292 { 1293 int i, j; 1294 int visibility; 1295 int color; 1296 int bytepos; 1297 1298 if ((pointer_shown) || (!pointer_enabled)) 1299 return; 1300 1301 /* Save image under the pointer. */ 1302 if (pointer_vport == -1) { 1303 pointer_vport = vport_create(pointer_x, pointer_y, pointer_width, pointer_height); 1304 if (pointer_vport < 0) 1305 return; 1306 } else { 1307 viewports[pointer_vport].x = pointer_x; 1308 viewports[pointer_vport].y = pointer_y; 1309 } 1310 1311 if (pointer_pixmap == -1) 1312 pointer_pixmap = save_vp_to_pixmap(&viewports[pointer_vport]); 1313 else 1314 copy_vp_to_pixmap(&viewports[pointer_vport], &pixmaps[pointer_pixmap]); 1315 1316 /* Draw mouse pointer. */ 1317 for (i = 0; i < pointer_height; i++) 1318 for (j = 0; j < pointer_width; j++) { 1319 bytepos = i * ((pointer_width - 1) / 8 + 1) + j / 8; 1320 visibility = pointer_mask_bits[bytepos] & 1321 (1 << (j % 8)); 1322 if (visibility) { 1323 color = pointer_bits[bytepos] & 1324 (1 << (j % 8)) ? 0 : 0xffffff; 1325 if (pointer_x + j < screen.xres && pointer_y + 1326 i < screen.yres) 1327 putpixel(&viewports[0], pointer_x + j, 1328 pointer_y + i, color); 1329 } 1330 } 1331 pointer_shown = 1; 1332 } 1333 1334 1335 static void mouse_hide(void) 1336 { 1337 /* Restore image under the pointer. */ 1338 if (pointer_shown) { 1339 draw_pixmap(pointer_vport, pointer_pixmap); 1340 pointer_shown = 0; 1341 } 1342 } 1343 1344 1345 static void mouse_move(unsigned int x, unsigned int y) 1346 { 1347 mouse_hide(); 1348 pointer_x = x % screen.xres; 1349 pointer_y = y % screen.yres; 1350 mouse_show(); 1351 } 1352 1353 1354 static int anim_handle(ipc_callid_t callid, ipc_call_t *call, int vp) 1355 { 1356 bool handled = true; 1357 int retval = EOK; 1358 int i, nvp; 1359 int newval; 1360 1361 switch (IPC_GET_IMETHOD(*call)) { 1362 case FB_ANIM_CREATE: 1363 nvp = IPC_GET_ARG1(*call); 1364 if (nvp == -1) 1365 nvp = vp; 1366 if (nvp >= MAX_VIEWPORTS || nvp < 0 || 1367 !viewports[nvp].initialized) { 1368 retval = EINVAL; 1369 break; 1370 } 1371 for (i = 0; i < MAX_ANIMATIONS; i++) { 1372 if (!animations[i].initialized) 1373 break; 1374 } 1375 if (i == MAX_ANIMATIONS) { 1376 retval = ELIMIT; 1377 break; 1378 } 1379 animations[i].initialized = 1; 1380 animations[i].animlen = 0; 1381 animations[i].pos = 0; 1382 animations[i].enabled = 0; 1383 animations[i].vp = nvp; 1384 retval = i; 1385 break; 1386 case FB_ANIM_DROP: 1387 i = IPC_GET_ARG1(*call); 1388 if (i >= MAX_ANIMATIONS || i < 0) { 1389 retval = EINVAL; 1390 break; 1391 } 1392 animations[i].initialized = 0; 1393 break; 1394 case FB_ANIM_ADDPIXMAP: 1395 i = IPC_GET_ARG1(*call); 1396 if (i >= MAX_ANIMATIONS || i < 0 || 1397 !animations[i].initialized) { 1398 retval = EINVAL; 1399 break; 1400 } 1401 if (animations[i].animlen == MAX_ANIM_LEN) { 1402 retval = ELIMIT; 1403 break; 1404 } 1405 newval = IPC_GET_ARG2(*call); 1406 if (newval < 0 || newval > MAX_PIXMAPS || 1407 !pixmaps[newval].data) { 1408 retval = EINVAL; 1409 break; 1410 } 1411 animations[i].pixmaps[animations[i].animlen++] = newval; 1412 break; 1413 case FB_ANIM_CHGVP: 1414 i = IPC_GET_ARG1(*call); 1415 if (i >= MAX_ANIMATIONS || i < 0) { 1416 retval = EINVAL; 1417 break; 1418 } 1419 nvp = IPC_GET_ARG2(*call); 1420 if (nvp == -1) 1421 nvp = vp; 1422 if (nvp >= MAX_VIEWPORTS || nvp < 0 || 1423 !viewports[nvp].initialized) { 1424 retval = EINVAL; 1425 break; 1426 } 1427 animations[i].vp = nvp; 1428 break; 1429 case FB_ANIM_START: 1430 case FB_ANIM_STOP: 1431 i = IPC_GET_ARG1(*call); 1432 if (i >= MAX_ANIMATIONS || i < 0) { 1433 retval = EINVAL; 1434 break; 1435 } 1436 newval = (IPC_GET_IMETHOD(*call) == FB_ANIM_START); 1437 if (newval ^ animations[i].enabled) { 1438 animations[i].enabled = newval; 1439 anims_enabled += newval ? 1 : -1; 1440 } 1441 break; 1442 default: 1443 handled = 0; 1444 } 1445 if (handled) 1446 async_answer_0(callid, retval); 1447 return handled; 1448 } 1449 1450 1451 /** Handler for messages concerning pixmap handling 1452 * 1453 */ 1454 static int pixmap_handle(ipc_callid_t callid, ipc_call_t *call, int vp) 1455 { 1456 bool handled = true; 1457 int retval = EOK; 1458 int i, nvp; 1459 1460 switch (IPC_GET_IMETHOD(*call)) { 1461 case FB_VP_DRAW_PIXMAP: 1462 nvp = IPC_GET_ARG1(*call); 1463 if (nvp == -1) 1464 nvp = vp; 1465 if (nvp < 0 || nvp >= MAX_VIEWPORTS || 1466 !viewports[nvp].initialized) { 1467 retval = EINVAL; 1468 break; 1469 } 1470 i = IPC_GET_ARG2(*call); 1471 retval = draw_pixmap(nvp, i); 1472 break; 1473 case FB_VP2PIXMAP: 1474 nvp = IPC_GET_ARG1(*call); 1475 if (nvp == -1) 1476 nvp = vp; 1477 if (nvp < 0 || nvp >= MAX_VIEWPORTS || 1478 !viewports[nvp].initialized) 1479 retval = EINVAL; 1480 else 1481 retval = save_vp_to_pixmap(&viewports[nvp]); 1482 break; 1483 case FB_DROP_PIXMAP: 1484 i = IPC_GET_ARG1(*call); 1485 if (i >= MAX_PIXMAPS) { 1486 retval = EINVAL; 1487 break; 1488 } 1489 if (pixmaps[i].data) { 1490 free(pixmaps[i].data); 1491 pixmaps[i].data = NULL; 1492 } 1493 break; 1494 default: 1495 handled = 0; 1496 } 1497 1498 if (handled) 1499 async_answer_0(callid, retval); 1500 return handled; 1501 1502 } 1503 1504 static int rgb_from_style(attr_rgb_t *rgb, int style) 1505 { 1506 switch (style) { 1507 case STYLE_NORMAL: 1508 rgb->fg_color = color_table[COLOR_BLACK]; 1509 rgb->bg_color = color_table[COLOR_WHITE]; 1510 break; 1511 case STYLE_EMPHASIS: 1512 rgb->fg_color = color_table[COLOR_RED]; 1513 rgb->bg_color = color_table[COLOR_WHITE]; 1514 break; 1515 case STYLE_INVERTED: 1516 rgb->fg_color = color_table[COLOR_WHITE]; 1517 rgb->bg_color = color_table[COLOR_BLACK]; 1518 break; 1519 case STYLE_SELECTED: 1520 rgb->fg_color = color_table[COLOR_WHITE]; 1521 rgb->bg_color = color_table[COLOR_RED]; 1522 break; 1523 default: 1524 return EINVAL; 1525 } 1526 1527 return EOK; 1528 } 1529 1530 static int rgb_from_idx(attr_rgb_t *rgb, sysarg_t fg_color, 1531 sysarg_t bg_color, sysarg_t flags) 1532 { 1533 fg_color = (fg_color & 7) | ((flags & CATTR_BRIGHT) ? 8 : 0); 1534 bg_color = (bg_color & 7) | ((flags & CATTR_BRIGHT) ? 8 : 0); 1535 1536 rgb->fg_color = color_table[fg_color]; 1537 rgb->bg_color = color_table[bg_color]; 1538 1539 return EOK; 1540 } 1541 1542 static int rgb_from_attr(attr_rgb_t *rgb, const attrs_t *a) 1543 { 1544 int rc; 1545 1546 switch (a->t) { 1547 case at_style: 1548 rc = rgb_from_style(rgb, a->a.s.style); 1549 break; 1550 case at_idx: 1551 rc = rgb_from_idx(rgb, a->a.i.fg_color, 1552 a->a.i.bg_color, a->a.i.flags); 1553 break; 1554 case at_rgb: 1555 *rgb = a->a.r; 1556 rc = EOK; 1557 break; 1558 } 1559 1560 return rc; 1561 } 1562 1563 static int fb_set_style(viewport_t *vport, sysarg_t style) 1564 { 1565 return rgb_from_style(&vport->attr, (int) style); 1566 } 1567 1568 static int fb_set_color(viewport_t *vport, sysarg_t fg_color, 1569 sysarg_t bg_color, sysarg_t flags) 1570 { 1571 return rgb_from_idx(&vport->attr, fg_color, bg_color, flags); 1572 } 1573 1574 /** Function for handling connections to FB 1575 * 1576 */ 1577 static void fb_client_connection(ipc_callid_t iid, ipc_call_t *icall) 1578 { 1579 unsigned int vp = 0; 1580 viewport_t *vport = &viewports[vp]; 1581 1582 if (client_connected) { 1583 async_answer_0(iid, ELIMIT); 1584 return; 1585 } 1586 1587 /* Accept connection */ 1588 client_connected = true; 1589 async_answer_0(iid, EOK); 1590 1591 while (true) { 1592 ipc_callid_t callid; 1593 ipc_call_t call; 1594 int retval; 1595 unsigned int i; 1596 int scroll; 1597 wchar_t ch; 1598 unsigned int col, row; 1599 1600 if ((vport->cursor_active) || (anims_enabled)) 1601 callid = async_get_call_timeout(&call, 250000); 1602 else 1603 callid = async_get_call(&call); 1604 1605 mouse_hide(); 1606 if (!callid) { 1607 cursor_blink(vport); 1608 anims_tick(); 1609 mouse_show(); 1610 continue; 1611 } 1612 1613 if (shm_handle(callid, &call, vp)) 1614 continue; 1615 1616 if (pixmap_handle(callid, &call, vp)) 1617 continue; 1618 1619 if (anim_handle(callid, &call, vp)) 1620 continue; 874 } 1621 875 1622 876 switch (IPC_GET_IMETHOD(call)) { 1623 case IPC_M_PHONE_HUNGUP: 1624 client_connected = false; 1625 1626 /* Cleanup other viewports */ 1627 for (i = 1; i < MAX_VIEWPORTS; i++) 1628 vport->initialized = false; 1629 1630 /* Exit thread */ 1631 return; 1632 1633 case FB_PUTCHAR: 1634 ch = IPC_GET_ARG1(call); 1635 col = IPC_GET_ARG2(call); 1636 row = IPC_GET_ARG3(call); 1637 1638 if ((col >= vport->cols) || (row >= vport->rows)) { 1639 retval = EINVAL; 1640 break; 1641 } 1642 async_answer_0(callid, EOK); 1643 1644 draw_char(vport, ch, col, row); 1645 1646 /* Message already answered */ 1647 continue; 1648 case FB_CLEAR: 1649 vport_clear(vport); 1650 cursor_show(vport); 1651 retval = EOK; 1652 break; 1653 case FB_CURSOR_GOTO: 1654 col = IPC_GET_ARG1(call); 1655 row = IPC_GET_ARG2(call); 1656 1657 if ((col >= vport->cols) || (row >= vport->rows)) { 1658 retval = EINVAL; 1659 break; 1660 } 1661 retval = EOK; 1662 1663 cursor_hide(vport); 1664 vport->cur_col = col; 1665 vport->cur_row = row; 1666 cursor_show(vport); 1667 break; 1668 case FB_CURSOR_VISIBILITY: 1669 cursor_hide(vport); 1670 vport->cursor_active = IPC_GET_ARG1(call); 1671 cursor_show(vport); 1672 retval = EOK; 1673 break; 1674 case FB_GET_CSIZE: 1675 async_answer_2(callid, EOK, vport->cols, vport->rows); 1676 continue; 1677 case FB_GET_COLOR_CAP: 1678 async_answer_1(callid, EOK, FB_CCAP_RGB); 1679 continue; 1680 case FB_SCROLL: 1681 scroll = IPC_GET_ARG1(call); 1682 if ((scroll > (int) vport->rows) || (scroll < (-(int) vport->rows))) { 1683 retval = EINVAL; 1684 break; 1685 } 1686 cursor_hide(vport); 1687 vport_scroll(vport, scroll); 1688 cursor_show(vport); 1689 retval = EOK; 1690 break; 1691 case FB_VIEWPORT_SWITCH: 1692 i = IPC_GET_ARG1(call); 1693 if (i >= MAX_VIEWPORTS) { 1694 retval = EINVAL; 1695 break; 1696 } 1697 if (!viewports[i].initialized) { 1698 retval = EADDRNOTAVAIL; 1699 break; 1700 } 1701 cursor_hide(vport); 1702 vp = i; 1703 vport = &viewports[vp]; 1704 cursor_show(vport); 1705 retval = EOK; 1706 break; 1707 case FB_VIEWPORT_CREATE: 1708 retval = vport_create(IPC_GET_ARG1(call) >> 16, 1709 IPC_GET_ARG1(call) & 0xffff, 1710 IPC_GET_ARG2(call) >> 16, 1711 IPC_GET_ARG2(call) & 0xffff); 1712 break; 1713 case FB_VIEWPORT_DELETE: 1714 i = IPC_GET_ARG1(call); 1715 if (i >= MAX_VIEWPORTS) { 1716 retval = EINVAL; 1717 break; 1718 } 1719 if (!viewports[i].initialized) { 1720 retval = EADDRNOTAVAIL; 1721 break; 1722 } 1723 viewports[i].initialized = false; 1724 if (viewports[i].bgpixel) 1725 free(viewports[i].bgpixel); 1726 if (viewports[i].backbuf) 1727 free(viewports[i].backbuf); 1728 retval = EOK; 1729 break; 1730 case FB_SET_STYLE: 1731 retval = fb_set_style(vport, IPC_GET_ARG1(call)); 1732 break; 1733 case FB_SET_COLOR: 1734 retval = fb_set_color(vport, IPC_GET_ARG1(call), 1735 IPC_GET_ARG2(call), IPC_GET_ARG3(call)); 1736 break; 1737 case FB_SET_RGB_COLOR: 1738 vport->attr.fg_color = IPC_GET_ARG1(call); 1739 vport->attr.bg_color = IPC_GET_ARG2(call); 1740 retval = EOK; 1741 break; 1742 case FB_GET_RESOLUTION: 1743 async_answer_2(callid, EOK, screen.xres, screen.yres); 1744 continue; 1745 case FB_POINTER_MOVE: 1746 pointer_enabled = true; 1747 mouse_move(IPC_GET_ARG1(call), IPC_GET_ARG2(call)); 1748 retval = EOK; 1749 break; 1750 case FB_SCREEN_YIELD: 1751 case FB_SCREEN_RECLAIM: 1752 retval = EOK; 1753 break; 1754 default: 1755 retval = ENOENT; 1756 } 1757 async_answer_0(callid, retval); 1758 } 1759 } 1760 1761 /** Initialization of framebuffer 1762 * 1763 */ 1764 int fb_init(void) 1765 { 1766 async_set_client_connection(fb_client_connection); 1767 1768 sysarg_t fb_ph_addr; 1769 if (sysinfo_get_value("fb.address.physical", &fb_ph_addr) != EOK) 1770 return -1; 1771 1772 sysarg_t fb_offset; 1773 if (sysinfo_get_value("fb.offset", &fb_offset) != EOK) 1774 fb_offset = 0; 1775 1776 sysarg_t fb_width; 1777 if (sysinfo_get_value("fb.width", &fb_width) != EOK) 1778 return -1; 1779 1780 sysarg_t fb_height; 1781 if (sysinfo_get_value("fb.height", &fb_height) != EOK) 1782 return -1; 1783 1784 sysarg_t fb_scanline; 1785 if (sysinfo_get_value("fb.scanline", &fb_scanline) != EOK) 1786 return -1; 1787 1788 sysarg_t fb_visual; 1789 if (sysinfo_get_value("fb.visual", &fb_visual) != EOK) 1790 return -1; 1791 1792 sysarg_t fbsize = fb_scanline * fb_height; 1793 void *fb_addr = as_get_mappable_page(fbsize); 1794 1795 if (physmem_map((void *) fb_ph_addr + fb_offset, fb_addr, 1796 ALIGN_UP(fbsize, PAGE_SIZE) >> PAGE_WIDTH, AS_AREA_READ | AS_AREA_WRITE) != 0) 1797 return -1; 1798 1799 if (screen_init(fb_addr, fb_width, fb_height, fb_scanline, fb_visual)) 1800 return 0; 1801 1802 return -1; 1803 } 1804 1805 /** 1806 * @} 1807 */ 877 878 /* Screen methods */ 879 880 case FB_GET_RESOLUTION: 881 fbsrv_get_resolution(dev, callid, &call); 882 break; 883 case FB_YIELD: 884 fbsrv_yield(dev, callid, &call); 885 break; 886 case FB_CLAIM: 887 fbsrv_claim(dev, callid, &call); 888 break; 889 case FB_POINTER_UPDATE: 890 fbsrv_pointer_update(dev, callid, &call); 891 break; 892 893 /* Object methods */ 894 895 case FB_VP_CREATE: 896 fbsrv_vp_create(dev, callid, &call); 897 break; 898 case FB_VP_DESTROY: 899 fbsrv_vp_destroy(dev, callid, &call); 900 break; 901 case FB_FRONTBUF_CREATE: 902 fbsrv_frontbuf_create(dev, callid, &call); 903 break; 904 case FB_FRONTBUF_DESTROY: 905 fbsrv_frontbuf_destroy(dev, callid, &call); 906 break; 907 case FB_IMAGEMAP_CREATE: 908 fbsrv_imagemap_create(dev, callid, &call); 909 break; 910 case FB_IMAGEMAP_DESTROY: 911 fbsrv_imagemap_destroy(dev, callid, &call); 912 break; 913 case FB_SEQUENCE_CREATE: 914 fbsrv_sequence_create(dev, callid, &call); 915 break; 916 case FB_SEQUENCE_DESTROY: 917 fbsrv_sequence_destroy(dev, callid, &call); 918 break; 919 case FB_SEQUENCE_ADD_IMAGEMAP: 920 fbsrv_sequence_add_imagemap(dev, callid, &call); 921 break; 922 923 /* Viewport stateful methods */ 924 925 case FB_VP_FOCUS: 926 fbsrv_vp_focus(dev, callid, &call); 927 break; 928 case FB_VP_CLEAR: 929 fbsrv_vp_clear(dev, callid, &call); 930 break; 931 case FB_VP_GET_DIMENSIONS: 932 fbsrv_vp_get_dimensions(dev, callid, &call); 933 break; 934 case FB_VP_GET_CAPS: 935 fbsrv_vp_get_caps(dev, callid, &call); 936 break; 937 938 /* Style methods (viewport specific) */ 939 940 case FB_VP_CURSOR_UPDATE: 941 fbsrv_vp_cursor_update(dev, callid, &call); 942 break; 943 case FB_VP_SET_STYLE: 944 fbsrv_vp_set_style(dev, callid, &call); 945 break; 946 case FB_VP_SET_COLOR: 947 fbsrv_vp_set_color(dev, callid, &call); 948 break; 949 case FB_VP_SET_RGB_COLOR: 950 fbsrv_vp_set_rgb_color(dev, callid, &call); 951 break; 952 953 /* Text output methods (viewport specific) */ 954 955 case FB_VP_PUTCHAR: 956 fbsrv_vp_putchar(dev, callid, &call); 957 break; 958 case FB_VP_UPDATE: 959 fbsrv_vp_update(dev, callid, &call); 960 break; 961 case FB_VP_DAMAGE: 962 fbsrv_vp_damage(dev, callid, &call); 963 break; 964 965 /* Image map methods (viewport specific) */ 966 967 case FB_VP_IMAGEMAP_DAMAGE: 968 fbsrv_vp_imagemap_damage(dev, callid, &call); 969 break; 970 971 /* Sequence methods (viewport specific) */ 972 973 case FB_VP_SEQUENCE_START: 974 fbsrv_vp_sequence_start(dev, callid, &call); 975 break; 976 case FB_VP_SEQUENCE_STOP: 977 fbsrv_vp_sequence_stop(dev, callid, &call); 978 break; 979 980 default: 981 async_answer_0(callid, EINVAL); 982 } 983 } 984 } 985 986 int main(int argc, char *argv[]) 987 { 988 printf("%s: HelenOS framebuffer service\n", NAME); 989 990 /* Register server */ 991 int rc = loc_server_register(NAME, client_connection); 992 if (rc != EOK) { 993 printf("%s: Unable to register driver (%d)\n", NAME, rc); 994 return 1; 995 } 996 997 ega_init(); 998 kchar_init(); 999 kfb_init(); 1000 niagara_init(); 1001 ski_init(); 1002 1003 printf("%s: Accepting connections\n", NAME); 1004 task_retval(0); 1005 async_manager(); 1006 1007 /* Never reached */ 1008 return 0; 1009 }
Note:
See TracChangeset
for help on using the changeset viewer.