source: mainline/uspace/srv/hid/console/gcons.c@ ffa2c8ef

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ffa2c8ef was ffa2c8ef, checked in by Martin Decky <martin@…>, 14 years ago

do not intermix low-level IPC methods with async framework methods

  • Property mode set to 100644
File size: 13.0 KB
RevLine 
[b1f51f0]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[b1f51f0]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[ce5bcb4]29/** @addtogroup console
[1601f3c]30 * @{
[ce5bcb4]31 */
32/** @file
33 */
34
[b1f51f0]35#include <ipc/fb.h>
[e1c4849]36#include <async.h>
37#include <stdio.h>
[2def788]38#include <sys/mman.h>
[19f857a]39#include <str.h>
[a7d2d78]40#include <align.h>
[424cd43]41#include <bool.h>
[b1f51f0]42
43#include "console.h"
[e1c4849]44#include "gcons.h"
[b1f51f0]45
[1601f3c]46#define CONSOLE_TOP 66
47#define CONSOLE_MARGIN 6
[b1f51f0]48
[1601f3c]49#define STATUS_START 110
50#define STATUS_TOP 8
51#define STATUS_SPACE 4
52#define STATUS_WIDTH 48
53#define STATUS_HEIGHT 48
[b1f51f0]54
[9f1362d4]55#define COLOR_MAIN 0xffffff
56#define COLOR_FOREGROUND 0x202020
57#define COLOR_BACKGROUND 0xffffff
58
59extern char _binary_gfx_helenos_ppm_start[0];
60extern int _binary_gfx_helenos_ppm_size;
61extern char _binary_gfx_nameic_ppm_start[0];
62extern int _binary_gfx_nameic_ppm_size;
63
64extern char _binary_gfx_anim_1_ppm_start[0];
65extern int _binary_gfx_anim_1_ppm_size;
66extern char _binary_gfx_anim_2_ppm_start[0];
67extern int _binary_gfx_anim_2_ppm_size;
68extern char _binary_gfx_anim_3_ppm_start[0];
69extern int _binary_gfx_anim_3_ppm_size;
70extern char _binary_gfx_anim_4_ppm_start[0];
71extern int _binary_gfx_anim_4_ppm_size;
72
73extern char _binary_gfx_cons_selected_ppm_start[0];
74extern int _binary_gfx_cons_selected_ppm_size;
75extern char _binary_gfx_cons_idle_ppm_start[0];
76extern int _binary_gfx_cons_idle_ppm_size;
77extern char _binary_gfx_cons_has_data_ppm_start[0];
78extern int _binary_gfx_cons_has_data_ppm_size;
79extern char _binary_gfx_cons_kernel_ppm_start[0];
80extern int _binary_gfx_cons_kernel_ppm_size;
[e1c4849]81
[424cd43]82static bool use_gcons = false;
[96b02eb9]83static sysarg_t xres;
84static sysarg_t yres;
[b1f51f0]85
[a7d2d78]86enum butstate {
87 CONS_DISCONNECTED = 0,
88 CONS_SELECTED,
89 CONS_IDLE,
90 CONS_HAS_DATA,
91 CONS_KERNEL,
92 CONS_DISCONNECTED_SEL,
93 CONS_LAST
94};
95
[b1f51f0]96static int console_vp;
97static int cstatus_vp[CONSOLE_COUNT];
[a7d2d78]98static enum butstate console_state[CONSOLE_COUNT];
[b1f51f0]99
100static int fbphone;
101
[a7d2d78]102/** List of pixmaps identifying these icons */
[00bb6965]103static int ic_pixmaps[CONS_LAST] = {-1, -1, -1, -1, -1, -1};
[1fd7700]104static int animation = -1;
[e1c4849]105
[424cd43]106static size_t active_console = 0;
107
[96b02eb9]108static sysarg_t mouse_x = 0;
109static sysarg_t mouse_y= 0;
[424cd43]110
[9f1362d4]111static bool btn_pressed = false;
[96b02eb9]112static sysarg_t btn_x = 0;
113static sysarg_t btn_y = 0;
[e1c4849]114
[b1f51f0]115static void vp_switch(int vp)
116{
[76fca31]117 async_msg_1(fbphone, FB_VIEWPORT_SWITCH, vp);
[b1f51f0]118}
119
[e1c4849]120/** Create view port */
[96b02eb9]121static int vp_create(sysarg_t x, sysarg_t y, sysarg_t width, sysarg_t height)
[b1f51f0]122{
[0cc4313]123 return async_req_2_0(fbphone, FB_VIEWPORT_CREATE, (x << 16) | y,
124 (width << 16) | height);
[b1f51f0]125}
126
[e1c4849]127static void clear(void)
[b1f51f0]128{
[0cc4313]129 async_msg_0(fbphone, FB_CLEAR);
[b1f51f0]130}
131
[424cd43]132static void set_rgb_color(uint32_t fgcolor, uint32_t bgcolor)
[e1c4849]133{
[9805cde]134 async_msg_2(fbphone, FB_SET_RGB_COLOR, fgcolor, bgcolor);
[e1c4849]135}
136
[d530237a]137/** Transparent putchar */
[96b02eb9]138static void tran_putch(wchar_t ch, sysarg_t col, sysarg_t row)
[e1c4849]139{
[424cd43]140 async_msg_3(fbphone, FB_PUTCHAR, ch, col, row);
[e1c4849]141}
142
[d530237a]143/** Redraw the button showing state of a given console */
[424cd43]144static void redraw_state(size_t index)
[b1f51f0]145{
[424cd43]146 vp_switch(cstatus_vp[index]);
147
148 enum butstate state = console_state[index];
[1601f3c]149
[a7d2d78]150 if (ic_pixmaps[state] != -1)
[424cd43]151 async_msg_2(fbphone, FB_VP_DRAW_PIXMAP, cstatus_vp[index],
[0cc4313]152 ic_pixmaps[state]);
[1601f3c]153
[424cd43]154 if ((state != CONS_DISCONNECTED) && (state != CONS_KERNEL)
155 && (state != CONS_DISCONNECTED_SEL)) {
156
157 char data[5];
[7e752b2]158 snprintf(data, 5, "%zu", index + 1);
[424cd43]159
160 size_t i;
161 for (i = 0; data[i] != 0; i++)
162 tran_putch(data[i], 2 + i, 1);
[1601f3c]163 }
[b1f51f0]164}
165
[a7d2d78]166/** Notification run on changing console (except kernel console) */
[424cd43]167void gcons_change_console(size_t index)
[b1f51f0]168{
169 if (!use_gcons)
170 return;
[1601f3c]171
[a7d2d78]172 if (active_console == KERNEL_CONSOLE) {
[424cd43]173 size_t i;
174
[00bb6965]175 for (i = 0; i < CONSOLE_COUNT; i++)
[a7d2d78]176 redraw_state(i);
[424cd43]177
[70178b74]178 if (animation != -1)
[0cc4313]179 async_msg_1(fbphone, FB_ANIM_START, animation);
[a7d2d78]180 } else {
181 if (console_state[active_console] == CONS_DISCONNECTED_SEL)
182 console_state[active_console] = CONS_DISCONNECTED;
183 else
184 console_state[active_console] = CONS_IDLE;
[424cd43]185
[a7d2d78]186 redraw_state(active_console);
187 }
[1601f3c]188
[424cd43]189 active_console = index;
190
191 if ((console_state[index] == CONS_DISCONNECTED)
192 || (console_state[index] == CONS_DISCONNECTED_SEL))
193 console_state[index] = CONS_DISCONNECTED_SEL;
194 else
195 console_state[index] = CONS_SELECTED;
[1601f3c]196
[424cd43]197 redraw_state(index);
[b1f51f0]198 vp_switch(console_vp);
199}
200
[429acb9]201/** Notification function that gets called on new output to virtual console */
[424cd43]202void gcons_notify_char(size_t index)
[b1f51f0]203{
204 if (!use_gcons)
205 return;
[1601f3c]206
[424cd43]207 if ((index == active_console)
208 || (console_state[index] == CONS_HAS_DATA))
[d6cc453]209 return;
[1601f3c]210
[424cd43]211 console_state[index] = CONS_HAS_DATA;
[1601f3c]212
[a7d2d78]213 if (active_console == KERNEL_CONSOLE)
[429acb9]214 return;
[1601f3c]215
[424cd43]216 redraw_state(index);
[b1f51f0]217 vp_switch(console_vp);
[a7d2d78]218}
[429acb9]219
[e9073f2]220/** Notification function called on service disconnect from console */
[424cd43]221void gcons_notify_disconnect(size_t index)
[e9073f2]222{
223 if (!use_gcons)
224 return;
[1601f3c]225
[424cd43]226 if (index == active_console)
227 console_state[index] = CONS_DISCONNECTED_SEL;
[e9073f2]228 else
[424cd43]229 console_state[index] = CONS_DISCONNECTED;
[76fca31]230
[e9073f2]231 if (active_console == KERNEL_CONSOLE)
232 return;
[76fca31]233
[424cd43]234 redraw_state(index);
[e9073f2]235 vp_switch(console_vp);
236}
237
[d530237a]238/** Notification function called on console connect */
[424cd43]239void gcons_notify_connect(size_t index)
[a7d2d78]240{
241 if (!use_gcons)
242 return;
[1601f3c]243
[424cd43]244 if (index == active_console)
245 console_state[index] = CONS_SELECTED;
[a7d2d78]246 else
[424cd43]247 console_state[index] = CONS_IDLE;
[1601f3c]248
[a7d2d78]249 if (active_console == KERNEL_CONSOLE)
250 return;
[1601f3c]251
[424cd43]252 redraw_state(index);
[a7d2d78]253 vp_switch(console_vp);
[b1f51f0]254}
255
[429acb9]256/** Change to kernel console */
257void gcons_in_kernel(void)
258{
[70178b74]259 if (animation != -1)
[0cc4313]260 async_msg_1(fbphone, FB_ANIM_STOP, animation);
[76fca31]261
262 active_console = KERNEL_CONSOLE;
[429acb9]263 vp_switch(0);
264}
265
[424cd43]266/** Return x, where left <= x <= right && |a-x| == min(|a-x|) is smallest */
[1e816c8]267static inline ssize_t limit(ssize_t a, ssize_t left, ssize_t right)
[830ac99]268{
269 if (a < left)
270 a = left;
[424cd43]271
[830ac99]272 if (a >= right)
273 a = right - 1;
[424cd43]274
[830ac99]275 return a;
276}
277
[1f83244]278/** Handle mouse move
279 *
280 * @param dx Delta X of mouse move
281 * @param dy Delta Y of mouse move
282 */
[424cd43]283void gcons_mouse_move(ssize_t dx, ssize_t dy)
[830ac99]284{
[9f1362d4]285 ssize_t nx = (ssize_t) mouse_x + dx;
286 ssize_t ny = (ssize_t) mouse_y + dy;
[ecd2775]287
288 if (!use_gcons)
289 return;
[9f1362d4]290
291 mouse_x = (size_t) limit(nx, 0, xres);
292 mouse_y = (size_t) limit(ny, 0, yres);
293
[2ae1e6e]294 if (active_console != KERNEL_CONSOLE)
295 async_msg_2(fbphone, FB_POINTER_MOVE, mouse_x, mouse_y);
[1f83244]296}
297
[96b02eb9]298static int gcons_find_conbut(sysarg_t x, sysarg_t y)
[1f83244]299{
[96b02eb9]300 sysarg_t status_start = STATUS_START + (xres - 800) / 2;
[1601f3c]301
302 if ((y < STATUS_TOP) || (y >= STATUS_TOP + STATUS_HEIGHT))
[1f83244]303 return -1;
304
305 if (x < status_start)
306 return -1;
307
[00bb6965]308 if (x >= status_start + (STATUS_WIDTH + STATUS_SPACE) * CONSOLE_COUNT)
[1f83244]309 return -1;
[9f1362d4]310
[f15cb3c4]311 if (((x - status_start) % (STATUS_WIDTH + STATUS_SPACE)) < STATUS_SPACE)
[1f83244]312 return -1;
313
[96b02eb9]314 sysarg_t btn = (x - status_start) / (STATUS_WIDTH + STATUS_SPACE);
[9f1362d4]315
316 if (btn < CONSOLE_COUNT)
317 return btn;
318
319 return -1;
[1f83244]320}
[830ac99]321
[1f83244]322/** Handle mouse click
323 *
[424cd43]324 * @param state New state (true - pressed, false - depressed)
[9f1362d4]325 *
[1f83244]326 */
[424cd43]327int gcons_mouse_btn(bool state)
[1f83244]328{
[9f1362d4]329 /* Ignore mouse clicks if no buttons
330 are drawn at all */
331 if (xres < 800)
332 return -1;
[1601f3c]333
[1f83244]334 if (state) {
[9f1362d4]335 int conbut = gcons_find_conbut(mouse_x, mouse_y);
[1f83244]336 if (conbut != -1) {
[424cd43]337 btn_pressed = true;
[1f83244]338 btn_x = mouse_x;
339 btn_y = mouse_y;
340 }
341 return -1;
[1601f3c]342 }
343
344 if ((!state) && (!btn_pressed))
[1f83244]345 return -1;
[1601f3c]346
[424cd43]347 btn_pressed = false;
[1601f3c]348
[9f1362d4]349 int conbut = gcons_find_conbut(mouse_x, mouse_y);
[1f83244]350 if (conbut == gcons_find_conbut(btn_x, btn_y))
351 return conbut;
[1601f3c]352
[1f83244]353 return -1;
[830ac99]354}
355
[429acb9]356/** Draw a PPM pixmap to framebuffer
357 *
358 * @param logo Pointer to PPM data
359 * @param size Size of PPM data
360 * @param x Coordinate of upper left corner
361 * @param y Coordinate of upper left corner
[9f1362d4]362 *
[429acb9]363 */
[96b02eb9]364static void draw_pixmap(char *logo, size_t size, sysarg_t x, sysarg_t y)
[90f5d64]365{
366 /* Create area */
[9f1362d4]367 char *shm = mmap(NULL, size, PROTO_READ | PROTO_WRITE, MAP_SHARED |
[0cc4313]368 MAP_ANONYMOUS, 0, 0);
[90f5d64]369 if (shm == MAP_FAILED)
370 return;
[1601f3c]371
[90f5d64]372 memcpy(shm, logo, size);
[1601f3c]373
[90f5d64]374 /* Send area */
[96b02eb9]375 int rc = async_req_1_0(fbphone, FB_PREPARE_SHM, (sysarg_t) shm);
[90f5d64]376 if (rc)
377 goto exit;
[1601f3c]378
[0da4e41]379 rc = async_share_out_start(fbphone, shm, PROTO_READ);
[90f5d64]380 if (rc)
381 goto drop;
[1601f3c]382
[90f5d64]383 /* Draw logo */
[085bd54]384 async_msg_2(fbphone, FB_DRAW_PPM, x, y);
[1601f3c]385
[90f5d64]386drop:
387 /* Drop area */
[0cc4313]388 async_msg_0(fbphone, FB_DROP_SHM);
[1601f3c]389
390exit:
[90f5d64]391 /* Remove area */
392 munmap(shm, size);
393}
394
[76fca31]395/** Redraws console graphics */
396void gcons_redraw_console(void)
[b1f51f0]397{
398 if (!use_gcons)
399 return;
400
401 vp_switch(0);
[9f1362d4]402 set_rgb_color(COLOR_MAIN, COLOR_MAIN);
[e1c4849]403 clear();
[1601f3c]404 draw_pixmap(_binary_gfx_helenos_ppm_start,
405 (size_t) &_binary_gfx_helenos_ppm_size, xres - 66, 2);
406 draw_pixmap(_binary_gfx_nameic_ppm_start,
407 (size_t) &_binary_gfx_nameic_ppm_size, 5, 17);
[76fca31]408
[9f1362d4]409 unsigned int i;
[0cc4313]410 for (i = 0; i < CONSOLE_COUNT; i++)
[a7d2d78]411 redraw_state(i);
[1601f3c]412
[b1f51f0]413 vp_switch(console_vp);
414}
415
[d530237a]416/** Creates a pixmap on framebuffer
417 *
418 * @param data PPM data
419 * @param size PPM data size
[424cd43]420 *
[d530237a]421 * @return Pixmap identification
[424cd43]422 *
[d530237a]423 */
[424cd43]424static int make_pixmap(char *data, size_t size)
[a7d2d78]425{
426 /* Create area */
[9f1362d4]427 char *shm = mmap(NULL, size, PROTO_READ | PROTO_WRITE, MAP_SHARED |
[0cc4313]428 MAP_ANONYMOUS, 0, 0);
[a7d2d78]429 if (shm == MAP_FAILED)
430 return -1;
[1601f3c]431
[a7d2d78]432 memcpy(shm, data, size);
[1601f3c]433
[9f1362d4]434 int pxid = -1;
435
[a7d2d78]436 /* Send area */
[96b02eb9]437 int rc = async_req_1_0(fbphone, FB_PREPARE_SHM, (sysarg_t) shm);
[a7d2d78]438 if (rc)
439 goto exit;
[1601f3c]440
[0da4e41]441 rc = async_share_out_start(fbphone, shm, PROTO_READ);
[a7d2d78]442 if (rc)
443 goto drop;
[1601f3c]444
[a7d2d78]445 /* Obtain pixmap */
[0cc4313]446 rc = async_req_0_0(fbphone, FB_SHM2PIXMAP);
[a7d2d78]447 if (rc < 0)
448 goto drop;
[1601f3c]449
[a7d2d78]450 pxid = rc;
[1601f3c]451
[a7d2d78]452drop:
453 /* Drop area */
[0cc4313]454 async_msg_0(fbphone, FB_DROP_SHM);
[1601f3c]455
456exit:
[a7d2d78]457 /* Remove area */
458 munmap(shm, size);
[1601f3c]459
[a7d2d78]460 return pxid;
461}
462
[1fd7700]463static void make_anim(void)
464{
[9f1362d4]465 int an = async_req_1_0(fbphone, FB_ANIM_CREATE,
466 cstatus_vp[KERNEL_CONSOLE]);
[1fd7700]467 if (an < 0)
468 return;
[1601f3c]469
470 int pm = make_pixmap(_binary_gfx_anim_1_ppm_start,
[36e9cd1]471 (size_t) &_binary_gfx_anim_1_ppm_size);
[1fd7700]472 async_msg_2(fbphone, FB_ANIM_ADDPIXMAP, an, pm);
[1601f3c]473
474 pm = make_pixmap(_binary_gfx_anim_2_ppm_start,
[36e9cd1]475 (size_t) &_binary_gfx_anim_2_ppm_size);
[1fd7700]476 async_msg_2(fbphone, FB_ANIM_ADDPIXMAP, an, pm);
[1601f3c]477
478 pm = make_pixmap(_binary_gfx_anim_3_ppm_start,
[36e9cd1]479 (size_t) &_binary_gfx_anim_3_ppm_size);
[1fd7700]480 async_msg_2(fbphone, FB_ANIM_ADDPIXMAP, an, pm);
[1601f3c]481
482 pm = make_pixmap(_binary_gfx_anim_4_ppm_start,
[36e9cd1]483 (size_t) &_binary_gfx_anim_4_ppm_size);
[1fd7700]484 async_msg_2(fbphone, FB_ANIM_ADDPIXMAP, an, pm);
[1601f3c]485
[0cc4313]486 async_msg_1(fbphone, FB_ANIM_START, an);
[1601f3c]487
[1fd7700]488 animation = an;
489}
490
[b1f51f0]491/** Initialize nice graphical console environment */
492void gcons_init(int phone)
493{
494 fbphone = phone;
[76fca31]495
[424cd43]496 int rc = async_req_0_2(phone, FB_GET_RESOLUTION, &xres, &yres);
[b1f51f0]497 if (rc)
498 return;
499
[76fca31]500 if ((xres < 800) || (yres < 600))
[b1f51f0]501 return;
[76fca31]502
[1601f3c]503 /* Create console viewport */
504
[a7d2d78]505 /* Align width & height to character size */
[d7baee6]506 console_vp = vp_create(CONSOLE_MARGIN, CONSOLE_TOP,
[0cc4313]507 ALIGN_DOWN(xres - 2 * CONSOLE_MARGIN, 8),
508 ALIGN_DOWN(yres - (CONSOLE_TOP + CONSOLE_MARGIN), 16));
[424cd43]509
[b1f51f0]510 if (console_vp < 0)
511 return;
512
513 /* Create status buttons */
[96b02eb9]514 sysarg_t status_start = STATUS_START + (xres - 800) / 2;
[424cd43]515 size_t i;
[00bb6965]516 for (i = 0; i < CONSOLE_COUNT; i++) {
[d7baee6]517 cstatus_vp[i] = vp_create(status_start + CONSOLE_MARGIN +
[0cc4313]518 i * (STATUS_WIDTH + STATUS_SPACE), STATUS_TOP,
519 STATUS_WIDTH, STATUS_HEIGHT);
[424cd43]520
[b1f51f0]521 if (cstatus_vp[i] < 0)
522 return;
[424cd43]523
[a7d2d78]524 vp_switch(cstatus_vp[i]);
[9f1362d4]525 set_rgb_color(COLOR_FOREGROUND, COLOR_BACKGROUND);
[b1f51f0]526 }
527
[a7d2d78]528 /* Initialize icons */
[00bb6965]529 ic_pixmaps[CONS_SELECTED] =
[1601f3c]530 make_pixmap(_binary_gfx_cons_selected_ppm_start,
[424cd43]531 (size_t) &_binary_gfx_cons_selected_ppm_size);
[1601f3c]532 ic_pixmaps[CONS_IDLE] =
533 make_pixmap(_binary_gfx_cons_idle_ppm_start,
[424cd43]534 (size_t) &_binary_gfx_cons_idle_ppm_size);
[00bb6965]535 ic_pixmaps[CONS_HAS_DATA] =
[1601f3c]536 make_pixmap(_binary_gfx_cons_has_data_ppm_start,
[424cd43]537 (size_t) &_binary_gfx_cons_has_data_ppm_size);
[00bb6965]538 ic_pixmaps[CONS_DISCONNECTED] =
[1601f3c]539 make_pixmap(_binary_gfx_cons_idle_ppm_start,
[424cd43]540 (size_t) &_binary_gfx_cons_idle_ppm_size);
[1601f3c]541 ic_pixmaps[CONS_KERNEL] =
542 make_pixmap(_binary_gfx_cons_kernel_ppm_start,
[424cd43]543 (size_t) &_binary_gfx_cons_kernel_ppm_size);
[a7d2d78]544 ic_pixmaps[CONS_DISCONNECTED_SEL] = ic_pixmaps[CONS_SELECTED];
[1fd7700]545
546 make_anim();
[76fca31]547
[424cd43]548 use_gcons = true;
[a7d2d78]549 console_state[0] = CONS_DISCONNECTED_SEL;
550 console_state[KERNEL_CONSOLE] = CONS_KERNEL;
[424cd43]551
[1035437]552 vp_switch(console_vp);
[b1f51f0]553}
[76fca31]554
[ce5bcb4]555/** @}
556 */
Note: See TracBrowser for help on using the repository browser.