blob: 199ba69101d532c4a6658b5c3007d9c4caff1a30 [file] [log] [blame]
bellarde7f0ad52004-07-14 17:28:59 +00001/*
2 * QEMU graphical console
ths5fafdf22007-09-16 21:08:06 +00003 *
bellarde7f0ad52004-07-14 17:28:59 +00004 * Copyright (c) 2004 Fabrice Bellard
ths5fafdf22007-09-16 21:08:06 +00005 *
bellarde7f0ad52004-07-14 17:28:59 +00006 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
pbrook87ecb682007-11-17 17:14:51 +000024#include "qemu-common.h"
Paolo Bonzini28ecbae2012-11-28 12:06:30 +010025#include "ui/console.h"
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +020026#include "hw/qdev-core.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010027#include "qemu/timer.h"
Luiz Capitulinoad39cf62012-05-24 13:48:23 -030028#include "qmp-commands.h"
Paolo Bonzinidccfcd02013-04-08 16:55:25 +020029#include "sysemu/char.h"
bellarde7f0ad52004-07-14 17:28:59 +000030
pbrook6d6f7c22006-03-11 15:35:30 +000031//#define DEBUG_CONSOLE
bellarde7f0ad52004-07-14 17:28:59 +000032#define DEFAULT_BACKSCROLL 512
33#define MAX_CONSOLES 12
Jan Kiszkabf1bed82012-07-10 22:00:55 +020034#define CONSOLE_CURSOR_PERIOD 500
bellarde7f0ad52004-07-14 17:28:59 +000035
pbrook6d6f7c22006-03-11 15:35:30 +000036typedef struct TextAttributes {
37 uint8_t fgcol:4;
38 uint8_t bgcol:4;
39 uint8_t bold:1;
40 uint8_t uline:1;
41 uint8_t blink:1;
42 uint8_t invers:1;
43 uint8_t unvisible:1;
44} TextAttributes;
45
bellarde7f0ad52004-07-14 17:28:59 +000046typedef struct TextCell {
47 uint8_t ch;
pbrook6d6f7c22006-03-11 15:35:30 +000048 TextAttributes t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +000049} TextCell;
50
51#define MAX_ESC_PARAMS 3
52
53enum TTYState {
54 TTY_STATE_NORM,
55 TTY_STATE_ESC,
56 TTY_STATE_CSI,
57};
58
bellarde15d7372006-06-25 16:26:29 +000059typedef struct QEMUFIFO {
60 uint8_t *buf;
61 int buf_size;
62 int count, wptr, rptr;
63} QEMUFIFO;
64
pbrook9596ebb2007-11-18 01:44:38 +000065static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000066{
67 int l, len;
68
69 l = f->buf_size - f->count;
70 if (len1 > l)
71 len1 = l;
72 len = len1;
73 while (len > 0) {
74 l = f->buf_size - f->wptr;
75 if (l > len)
76 l = len;
77 memcpy(f->buf + f->wptr, buf, l);
78 f->wptr += l;
79 if (f->wptr >= f->buf_size)
80 f->wptr = 0;
81 buf += l;
82 len -= l;
83 }
84 f->count += len1;
85 return len1;
86}
87
pbrook9596ebb2007-11-18 01:44:38 +000088static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000089{
90 int l, len;
91
92 if (len1 > f->count)
93 len1 = f->count;
94 len = len1;
95 while (len > 0) {
96 l = f->buf_size - f->rptr;
97 if (l > len)
98 l = len;
99 memcpy(buf, f->buf + f->rptr, l);
100 f->rptr += l;
101 if (f->rptr >= f->buf_size)
102 f->rptr = 0;
103 buf += l;
104 len -= l;
105 }
106 f->count -= len1;
107 return len1;
108}
109
thsaf3a9032007-07-11 23:14:59 +0000110typedef enum {
111 GRAPHIC_CONSOLE,
balrogc21bbcf2008-09-24 03:32:33 +0000112 TEXT_CONSOLE,
113 TEXT_CONSOLE_FIXED_SIZE
Anthony Liguoric227f092009-10-01 16:12:16 -0500114} console_type_t;
thsaf3a9032007-07-11 23:14:59 +0000115
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200116struct QemuConsole {
Gerd Hoffmann95be0662013-04-17 09:45:10 +0200117 Object parent;
118
Jan Kiszkaf81bdef2011-09-16 00:48:07 +0200119 int index;
Anthony Liguoric227f092009-10-01 16:12:16 -0500120 console_type_t console_type;
bellarde7f0ad52004-07-14 17:28:59 +0000121 DisplayState *ds;
Gerd Hoffmann321f0482013-03-12 14:39:22 +0100122 DisplaySurface *surface;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +0100123 int dcls;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200124
pbrook95219892006-04-09 01:06:34 +0000125 /* Graphic console state. */
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +0200126 Object *device;
Gerd Hoffmann380cd052013-03-13 14:04:18 +0100127 const GraphicHwOps *hw_ops;
pbrook95219892006-04-09 01:06:34 +0000128 void *hw;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200129
130 /* Text console state */
bellarde7f0ad52004-07-14 17:28:59 +0000131 int width;
132 int height;
133 int total_height;
134 int backscroll_height;
bellarde7f0ad52004-07-14 17:28:59 +0000135 int x, y;
thsadb47962007-01-16 23:02:36 +0000136 int x_saved, y_saved;
bellarde7f0ad52004-07-14 17:28:59 +0000137 int y_displayed;
138 int y_base;
pbrook6d6f7c22006-03-11 15:35:30 +0000139 TextAttributes t_attrib_default; /* default text attributes */
140 TextAttributes t_attrib; /* currently active text attributes */
bellarde7f0ad52004-07-14 17:28:59 +0000141 TextCell *cells;
balrog4d3b6f62008-02-10 16:33:14 +0000142 int text_x[2], text_y[2], cursor_invalidate;
Paolo Bonzini41048332010-12-23 13:42:52 +0100143 int echo;
Jan Kiszkabf1bed82012-07-10 22:00:55 +0200144 bool cursor_visible_phase;
145 QEMUTimer *cursor_timer;
bellarde7f0ad52004-07-14 17:28:59 +0000146
pbrook14778c22009-01-21 03:02:52 +0000147 int update_x0;
148 int update_y0;
149 int update_x1;
150 int update_y1;
151
bellarde7f0ad52004-07-14 17:28:59 +0000152 enum TTYState state;
153 int esc_params[MAX_ESC_PARAMS];
154 int nb_esc_params;
155
pbrooke5b0bc42007-01-27 23:46:43 +0000156 CharDriverState *chr;
bellarde15d7372006-06-25 16:26:29 +0000157 /* fifo for key pressed */
158 QEMUFIFO out_fifo;
159 uint8_t out_fifo_buf[16];
160 QEMUTimer *kbd_timer;
bellarde7f0ad52004-07-14 17:28:59 +0000161};
162
Gerd Hoffmann27be5582013-03-13 12:25:25 +0100163struct DisplayState {
164 struct QEMUTimer *gui_timer;
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100165 uint64_t last_update;
166 uint64_t update_interval;
167 bool refreshing;
Gerd Hoffmann27be5582013-03-13 12:25:25 +0100168 bool have_gfx;
169 bool have_text;
170
171 QLIST_HEAD(, DisplayChangeListener) listeners;
172};
173
Paolo Bonzini98b50082010-02-11 00:29:57 +0100174static DisplayState *display_state;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200175static QemuConsole *active_console;
176static QemuConsole *consoles[MAX_CONSOLES];
bellarde7f0ad52004-07-14 17:28:59 +0000177static int nb_consoles = 0;
178
Gerd Hoffmann64840c62013-03-07 17:08:29 +0100179static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100180static void dpy_refresh(DisplayState *s);
Gerd Hoffmann52090892013-04-23 15:44:31 +0200181static DisplayState *get_alloc_displaystate(void);
Gerd Hoffmann64840c62013-03-07 17:08:29 +0100182
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100183static void gui_update(void *opaque)
184{
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100185 uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
186 uint64_t dcl_interval;
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100187 DisplayState *ds = opaque;
188 DisplayChangeListener *dcl;
Gerd Hoffmanndea1b0b2013-03-19 15:01:02 +0100189 int i;
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100190
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100191 ds->refreshing = true;
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100192 dpy_refresh(ds);
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100193 ds->refreshing = false;
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100194
195 QLIST_FOREACH(dcl, &ds->listeners, next) {
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100196 dcl_interval = dcl->update_interval ?
197 dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
198 if (interval > dcl_interval) {
199 interval = dcl_interval;
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100200 }
201 }
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100202 if (ds->update_interval != interval) {
203 ds->update_interval = interval;
Gerd Hoffmanndea1b0b2013-03-19 15:01:02 +0100204 for (i = 0; i < nb_consoles; i++) {
205 if (consoles[i]->hw_ops->update_interval) {
206 consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval);
207 }
208 }
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +0100209 trace_console_refresh(interval);
210 }
Alex Blighbc72ad62013-08-21 16:03:08 +0100211 ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
212 timer_mod(ds->gui_timer, ds->last_update + interval);
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100213}
214
215static void gui_setup_refresh(DisplayState *ds)
216{
217 DisplayChangeListener *dcl;
218 bool need_timer = false;
219 bool have_gfx = false;
220 bool have_text = false;
221
222 QLIST_FOREACH(dcl, &ds->listeners, next) {
223 if (dcl->ops->dpy_refresh != NULL) {
224 need_timer = true;
225 }
226 if (dcl->ops->dpy_gfx_update != NULL) {
227 have_gfx = true;
228 }
229 if (dcl->ops->dpy_text_update != NULL) {
230 have_text = true;
231 }
232 }
233
234 if (need_timer && ds->gui_timer == NULL) {
Alex Blighbc72ad62013-08-21 16:03:08 +0100235 ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
236 timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100237 }
238 if (!need_timer && ds->gui_timer != NULL) {
Alex Blighbc72ad62013-08-21 16:03:08 +0100239 timer_del(ds->gui_timer);
240 timer_free(ds->gui_timer);
Gerd Hoffmann98a9ad92013-03-13 12:17:13 +0100241 ds->gui_timer = NULL;
242 }
243
244 ds->have_gfx = have_gfx;
245 ds->have_text = have_text;
246}
247
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100248void graphic_hw_update(QemuConsole *con)
pbrook95219892006-04-09 01:06:34 +0000249{
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100250 if (!con) {
251 con = active_console;
252 }
Gerd Hoffmann380cd052013-03-13 14:04:18 +0100253 if (con && con->hw_ops->gfx_update) {
254 con->hw_ops->gfx_update(con->hw);
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100255 }
pbrook95219892006-04-09 01:06:34 +0000256}
257
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100258void graphic_hw_invalidate(QemuConsole *con)
pbrook95219892006-04-09 01:06:34 +0000259{
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100260 if (!con) {
261 con = active_console;
262 }
Gerd Hoffmann380cd052013-03-13 14:04:18 +0100263 if (con && con->hw_ops->invalidate) {
264 con->hw_ops->invalidate(con->hw);
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100265 }
pbrook95219892006-04-09 01:06:34 +0000266}
267
Gerd Hoffmann2c62f082013-03-12 14:48:31 +0100268static void ppm_save(const char *filename, struct DisplaySurface *ds,
269 Error **errp)
270{
271 int width = pixman_image_get_width(ds->image);
272 int height = pixman_image_get_height(ds->image);
Gerd Hoffmanncdd5b932013-04-23 13:26:59 +0200273 int fd;
Gerd Hoffmann2c62f082013-03-12 14:48:31 +0100274 FILE *f;
275 int y;
276 int ret;
277 pixman_image_t *linebuf;
278
279 trace_ppm_save(filename, ds);
Gerd Hoffmanncdd5b932013-04-23 13:26:59 +0200280 fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
281 if (fd == -1) {
Gerd Hoffmann2c62f082013-03-12 14:48:31 +0100282 error_setg(errp, "failed to open file '%s': %s", filename,
283 strerror(errno));
284 return;
285 }
Gerd Hoffmanncdd5b932013-04-23 13:26:59 +0200286 f = fdopen(fd, "wb");
Gerd Hoffmann2c62f082013-03-12 14:48:31 +0100287 ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
288 if (ret < 0) {
289 linebuf = NULL;
290 goto write_err;
291 }
292 linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
293 for (y = 0; y < height; y++) {
294 qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
295 clearerr(f);
296 ret = fwrite(pixman_image_get_data(linebuf), 1,
297 pixman_image_get_stride(linebuf), f);
298 (void)ret;
299 if (ferror(f)) {
300 goto write_err;
301 }
302 }
303
304out:
305 qemu_pixman_image_unref(linebuf);
306 fclose(f);
307 return;
308
309write_err:
310 error_setg(errp, "failed to write to file '%s': %s", filename,
311 strerror(errno));
312 unlink(filename);
313 goto out;
314}
315
Luiz Capitulinoad39cf62012-05-24 13:48:23 -0300316void qmp_screendump(const char *filename, Error **errp)
pbrook95219892006-04-09 01:06:34 +0000317{
Gerd Hoffmann284d1c62013-03-15 15:45:54 +0100318 QemuConsole *con = qemu_console_lookup_by_index(0);
Gerd Hoffmann2c62f082013-03-12 14:48:31 +0100319 DisplaySurface *surface;
balrog8571c052008-07-19 13:04:26 +0000320
Gerd Hoffmann2c62f082013-03-12 14:48:31 +0100321 if (con == NULL) {
322 error_setg(errp, "There is no QemuConsole I can screendump from.");
323 return;
Jan Kiszkaf81bdef2011-09-16 00:48:07 +0200324 }
325
Gerd Hoffmann2c62f082013-03-12 14:48:31 +0100326 graphic_hw_update(con);
327 surface = qemu_console_surface(con);
328 ppm_save(filename, surface, errp);
pbrook95219892006-04-09 01:06:34 +0000329}
330
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100331void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
balrog4d3b6f62008-02-10 16:33:14 +0000332{
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +0100333 if (!con) {
334 con = active_console;
335 }
Gerd Hoffmann380cd052013-03-13 14:04:18 +0100336 if (con && con->hw_ops->text_update) {
337 con->hw_ops->text_update(con->hw, chardata);
338 }
balrog4d3b6f62008-02-10 16:33:14 +0000339}
340
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100341static void vga_fill_rect(QemuConsole *con,
342 int posx, int posy, int width, int height,
Gerd Hoffmanne27bd652013-03-08 08:43:24 +0100343 pixman_color_t color)
bellarde7f0ad52004-07-14 17:28:59 +0000344{
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100345 DisplaySurface *surface = qemu_console_surface(con);
Gerd Hoffmann68db6dc2013-03-06 15:43:23 +0100346 pixman_rectangle16_t rect = {
347 .x = posx, .y = posy, .width = width, .height = height
348 };
ths3b46e622007-09-17 08:09:54 +0000349
Gerd Hoffmann68db6dc2013-03-06 15:43:23 +0100350 pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
Gerd Hoffmanne27bd652013-03-08 08:43:24 +0100351 &color, 1, &rect);
bellarde7f0ad52004-07-14 17:28:59 +0000352}
353
354/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100355static void vga_bitblt(QemuConsole *con,
356 int xs, int ys, int xd, int yd, int w, int h)
bellarde7f0ad52004-07-14 17:28:59 +0000357{
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100358 DisplaySurface *surface = qemu_console_surface(con);
bellarde7f0ad52004-07-14 17:28:59 +0000359
Gerd Hoffmann68db6dc2013-03-06 15:43:23 +0100360 pixman_image_composite(PIXMAN_OP_SRC,
361 surface->image, NULL, surface->image,
362 xs, ys, 0, 0, xd, yd, w, h);
bellarde7f0ad52004-07-14 17:28:59 +0000363}
364
365/***********************************************************/
366/* basic char display */
367
368#define FONT_HEIGHT 16
369#define FONT_WIDTH 8
370
371#include "vgafont.h"
372
Devin J. Pohlydf00bed2011-09-07 15:44:36 -0400373#ifndef CONFIG_CURSES
pbrook6d6f7c22006-03-11 15:35:30 +0000374enum color_names {
375 COLOR_BLACK = 0,
376 COLOR_RED = 1,
377 COLOR_GREEN = 2,
378 COLOR_YELLOW = 3,
379 COLOR_BLUE = 4,
380 COLOR_MAGENTA = 5,
381 COLOR_CYAN = 6,
382 COLOR_WHITE = 7
383};
Devin J. Pohlydf00bed2011-09-07 15:44:36 -0400384#endif
pbrook6d6f7c22006-03-11 15:35:30 +0000385
Gerd Hoffmanne27bd652013-03-08 08:43:24 +0100386#define QEMU_RGB(r, g, b) \
387 { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
388
389static const pixman_color_t color_table_rgb[2][8] = {
pbrook6d6f7c22006-03-11 15:35:30 +0000390 { /* dark */
bellard26489842006-06-25 17:37:36 +0000391 QEMU_RGB(0x00, 0x00, 0x00), /* black */
392 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
393 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
394 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
395 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
396 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
397 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
398 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000399 },
400 { /* bright */
bellard26489842006-06-25 17:37:36 +0000401 QEMU_RGB(0x00, 0x00, 0x00), /* black */
402 QEMU_RGB(0xff, 0x00, 0x00), /* red */
403 QEMU_RGB(0x00, 0xff, 0x00), /* green */
404 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
405 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
406 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
407 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
408 QEMU_RGB(0xff, 0xff, 0xff), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000409 }
bellarde7f0ad52004-07-14 17:28:59 +0000410};
411
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100412static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000413 TextAttributes *t_attrib)
bellarde7f0ad52004-07-14 17:28:59 +0000414{
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100415 static pixman_image_t *glyphs[256];
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100416 DisplaySurface *surface = qemu_console_surface(s);
Gerd Hoffmanne27bd652013-03-08 08:43:24 +0100417 pixman_color_t fgcol, bgcol;
pbrook6d6f7c22006-03-11 15:35:30 +0000418
419 if (t_attrib->invers) {
Gerd Hoffmanncf6f0542013-03-06 09:50:51 +0100420 bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
421 fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
pbrook6d6f7c22006-03-11 15:35:30 +0000422 } else {
Gerd Hoffmanncf6f0542013-03-06 09:50:51 +0100423 fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
424 bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
pbrook6d6f7c22006-03-11 15:35:30 +0000425 }
bellarde7f0ad52004-07-14 17:28:59 +0000426
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100427 if (!glyphs[ch]) {
428 glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
bellarde7f0ad52004-07-14 17:28:59 +0000429 }
Gerd Hoffmann7d6ba012013-03-06 15:44:10 +0100430 qemu_pixman_glyph_render(glyphs[ch], surface->image,
Gerd Hoffmanne27bd652013-03-08 08:43:24 +0100431 &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
bellarde7f0ad52004-07-14 17:28:59 +0000432}
433
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200434static void text_console_resize(QemuConsole *s)
bellarde7f0ad52004-07-14 17:28:59 +0000435{
436 TextCell *cells, *c, *c1;
437 int w1, x, y, last_width;
438
439 last_width = s->width;
Gerd Hoffmann36671fb2013-03-13 10:14:52 +0100440 s->width = surface_width(s->surface) / FONT_WIDTH;
441 s->height = surface_height(s->surface) / FONT_HEIGHT;
bellarde7f0ad52004-07-14 17:28:59 +0000442
443 w1 = last_width;
444 if (s->width < w1)
445 w1 = s->width;
446
Anthony Liguori7267c092011-08-20 22:09:37 -0500447 cells = g_malloc(s->width * s->total_height * sizeof(TextCell));
bellarde7f0ad52004-07-14 17:28:59 +0000448 for(y = 0; y < s->total_height; y++) {
449 c = &cells[y * s->width];
450 if (w1 > 0) {
451 c1 = &s->cells[y * last_width];
452 for(x = 0; x < w1; x++) {
453 *c++ = *c1++;
454 }
455 }
456 for(x = w1; x < s->width; x++) {
457 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000458 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000459 c++;
460 }
461 }
Anthony Liguori7267c092011-08-20 22:09:37 -0500462 g_free(s->cells);
bellarde7f0ad52004-07-14 17:28:59 +0000463 s->cells = cells;
464}
465
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200466static inline void text_update_xy(QemuConsole *s, int x, int y)
balrog4d3b6f62008-02-10 16:33:14 +0000467{
468 s->text_x[0] = MIN(s->text_x[0], x);
469 s->text_x[1] = MAX(s->text_x[1], x);
470 s->text_y[0] = MIN(s->text_y[0], y);
471 s->text_y[1] = MAX(s->text_y[1], y);
472}
473
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200474static void invalidate_xy(QemuConsole *s, int x, int y)
pbrook14778c22009-01-21 03:02:52 +0000475{
476 if (s->update_x0 > x * FONT_WIDTH)
477 s->update_x0 = x * FONT_WIDTH;
478 if (s->update_y0 > y * FONT_HEIGHT)
479 s->update_y0 = y * FONT_HEIGHT;
480 if (s->update_x1 < (x + 1) * FONT_WIDTH)
481 s->update_x1 = (x + 1) * FONT_WIDTH;
482 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
483 s->update_y1 = (y + 1) * FONT_HEIGHT;
484}
485
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200486static void update_xy(QemuConsole *s, int x, int y)
bellarde7f0ad52004-07-14 17:28:59 +0000487{
488 TextCell *c;
489 int y1, y2;
490
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +0100491 if (!qemu_console_is_visible(s)) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100492 return;
493 }
balrog4d3b6f62008-02-10 16:33:14 +0000494
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100495 if (s->ds->have_text) {
496 text_update_xy(s, x, y);
497 }
498
499 if (s->ds->have_gfx) {
bellarde7f0ad52004-07-14 17:28:59 +0000500 y1 = (s->y_base + y) % s->total_height;
501 y2 = y1 - s->y_displayed;
502 if (y2 < 0)
503 y2 += s->total_height;
504 if (y2 < s->height) {
505 c = &s->cells[y1 * s->width + x];
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100506 vga_putcharxy(s, x, y2, c->ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000507 &(c->t_attrib));
pbrook14778c22009-01-21 03:02:52 +0000508 invalidate_xy(s, x, y2);
bellarde7f0ad52004-07-14 17:28:59 +0000509 }
510 }
511}
512
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200513static void console_show_cursor(QemuConsole *s, int show)
bellarde7f0ad52004-07-14 17:28:59 +0000514{
515 TextCell *c;
516 int y, y1;
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100517 int x = s->x;
bellarde7f0ad52004-07-14 17:28:59 +0000518
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +0100519 if (!qemu_console_is_visible(s)) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100520 return;
521 }
balrog4d3b6f62008-02-10 16:33:14 +0000522
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100523 if (s->ds->have_text) {
524 s->cursor_invalidate = 1;
525 }
balrog4d3b6f62008-02-10 16:33:14 +0000526
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100527 if (s->ds->have_gfx) {
thsed8276a2007-02-10 22:37:56 +0000528 if (x >= s->width) {
529 x = s->width - 1;
530 }
bellarde7f0ad52004-07-14 17:28:59 +0000531 y1 = (s->y_base + s->y) % s->total_height;
532 y = y1 - s->y_displayed;
533 if (y < 0)
534 y += s->total_height;
535 if (y < s->height) {
thsed8276a2007-02-10 22:37:56 +0000536 c = &s->cells[y1 * s->width + x];
Jan Kiszkabf1bed82012-07-10 22:00:55 +0200537 if (show && s->cursor_visible_phase) {
pbrook6d6f7c22006-03-11 15:35:30 +0000538 TextAttributes t_attrib = s->t_attrib_default;
539 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100540 vga_putcharxy(s, x, y, c->ch, &t_attrib);
bellarde7f0ad52004-07-14 17:28:59 +0000541 } else {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100542 vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
bellarde7f0ad52004-07-14 17:28:59 +0000543 }
pbrook14778c22009-01-21 03:02:52 +0000544 invalidate_xy(s, x, y);
bellarde7f0ad52004-07-14 17:28:59 +0000545 }
546 }
547}
548
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200549static void console_refresh(QemuConsole *s)
bellarde7f0ad52004-07-14 17:28:59 +0000550{
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100551 DisplaySurface *surface = qemu_console_surface(s);
bellarde7f0ad52004-07-14 17:28:59 +0000552 TextCell *c;
553 int x, y, y1;
554
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +0100555 if (!qemu_console_is_visible(s)) {
bellarde7f0ad52004-07-14 17:28:59 +0000556 return;
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +0100557 }
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200558
559 if (s->ds->have_text) {
balrog4d3b6f62008-02-10 16:33:14 +0000560 s->text_x[0] = 0;
561 s->text_y[0] = 0;
562 s->text_x[1] = s->width - 1;
563 s->text_y[1] = s->height - 1;
564 s->cursor_invalidate = 1;
balrog4d3b6f62008-02-10 16:33:14 +0000565 }
bellarde7f0ad52004-07-14 17:28:59 +0000566
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200567 if (s->ds->have_gfx) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100568 vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
Gerd Hoffmanncf6f0542013-03-06 09:50:51 +0100569 color_table_rgb[0][COLOR_BLACK]);
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200570 y1 = s->y_displayed;
571 for (y = 0; y < s->height; y++) {
572 c = s->cells + y1 * s->width;
573 for (x = 0; x < s->width; x++) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100574 vga_putcharxy(s, x, y, c->ch,
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200575 &(c->t_attrib));
576 c++;
577 }
578 if (++y1 == s->total_height) {
579 y1 = 0;
580 }
bellarde7f0ad52004-07-14 17:28:59 +0000581 }
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200582 console_show_cursor(s, 1);
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100583 dpy_gfx_update(s, 0, 0,
584 surface_width(surface), surface_height(surface));
bellarde7f0ad52004-07-14 17:28:59 +0000585 }
bellarde7f0ad52004-07-14 17:28:59 +0000586}
587
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +0100588static void console_scroll(QemuConsole *s, int ydelta)
bellarde7f0ad52004-07-14 17:28:59 +0000589{
bellarde7f0ad52004-07-14 17:28:59 +0000590 int i, y1;
ths3b46e622007-09-17 08:09:54 +0000591
bellarde7f0ad52004-07-14 17:28:59 +0000592 if (ydelta > 0) {
593 for(i = 0; i < ydelta; i++) {
594 if (s->y_displayed == s->y_base)
595 break;
596 if (++s->y_displayed == s->total_height)
597 s->y_displayed = 0;
598 }
599 } else {
600 ydelta = -ydelta;
601 i = s->backscroll_height;
602 if (i > s->total_height - s->height)
603 i = s->total_height - s->height;
604 y1 = s->y_base - i;
605 if (y1 < 0)
606 y1 += s->total_height;
607 for(i = 0; i < ydelta; i++) {
608 if (s->y_displayed == y1)
609 break;
610 if (--s->y_displayed < 0)
611 s->y_displayed = s->total_height - 1;
612 }
613 }
614 console_refresh(s);
615}
616
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200617static void console_put_lf(QemuConsole *s)
bellarde7f0ad52004-07-14 17:28:59 +0000618{
619 TextCell *c;
620 int x, y1;
621
bellarde7f0ad52004-07-14 17:28:59 +0000622 s->y++;
623 if (s->y >= s->height) {
624 s->y = s->height - 1;
pbrook6d6f7c22006-03-11 15:35:30 +0000625
bellarde7f0ad52004-07-14 17:28:59 +0000626 if (s->y_displayed == s->y_base) {
627 if (++s->y_displayed == s->total_height)
628 s->y_displayed = 0;
629 }
630 if (++s->y_base == s->total_height)
631 s->y_base = 0;
632 if (s->backscroll_height < s->total_height)
633 s->backscroll_height++;
634 y1 = (s->y_base + s->height - 1) % s->total_height;
635 c = &s->cells[y1 * s->width];
636 for(x = 0; x < s->width; x++) {
637 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000638 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000639 c++;
640 }
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +0100641 if (qemu_console_is_visible(s) && s->y_displayed == s->y_base) {
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100642 if (s->ds->have_text) {
balrog4d3b6f62008-02-10 16:33:14 +0000643 s->text_x[0] = 0;
644 s->text_y[0] = 0;
645 s->text_x[1] = s->width - 1;
646 s->text_y[1] = s->height - 1;
balrog4d3b6f62008-02-10 16:33:14 +0000647 }
648
Gerd Hoffmann1562e532013-03-06 13:40:47 +0100649 if (s->ds->have_gfx) {
650 vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
651 s->width * FONT_WIDTH,
652 (s->height - 1) * FONT_HEIGHT);
653 vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
654 s->width * FONT_WIDTH, FONT_HEIGHT,
655 color_table_rgb[0][s->t_attrib_default.bgcol]);
656 s->update_x0 = 0;
657 s->update_y0 = 0;
658 s->update_x1 = s->width * FONT_WIDTH;
659 s->update_y1 = s->height * FONT_HEIGHT;
660 }
bellarde7f0ad52004-07-14 17:28:59 +0000661 }
662 }
663}
664
pbrook6d6f7c22006-03-11 15:35:30 +0000665/* Set console attributes depending on the current escape codes.
666 * NOTE: I know this code is not very efficient (checking every color for it
667 * self) but it is more readable and better maintainable.
668 */
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200669static void console_handle_escape(QemuConsole *s)
pbrook6d6f7c22006-03-11 15:35:30 +0000670{
671 int i;
672
pbrook6d6f7c22006-03-11 15:35:30 +0000673 for (i=0; i<s->nb_esc_params; i++) {
674 switch (s->esc_params[i]) {
675 case 0: /* reset all console attributes to default */
676 s->t_attrib = s->t_attrib_default;
677 break;
678 case 1:
679 s->t_attrib.bold = 1;
680 break;
681 case 4:
682 s->t_attrib.uline = 1;
683 break;
684 case 5:
685 s->t_attrib.blink = 1;
686 break;
687 case 7:
688 s->t_attrib.invers = 1;
689 break;
690 case 8:
691 s->t_attrib.unvisible = 1;
692 break;
693 case 22:
694 s->t_attrib.bold = 0;
695 break;
696 case 24:
697 s->t_attrib.uline = 0;
698 break;
699 case 25:
700 s->t_attrib.blink = 0;
701 break;
702 case 27:
703 s->t_attrib.invers = 0;
704 break;
705 case 28:
706 s->t_attrib.unvisible = 0;
707 break;
708 /* set foreground color */
709 case 30:
710 s->t_attrib.fgcol=COLOR_BLACK;
711 break;
712 case 31:
713 s->t_attrib.fgcol=COLOR_RED;
714 break;
715 case 32:
716 s->t_attrib.fgcol=COLOR_GREEN;
717 break;
718 case 33:
719 s->t_attrib.fgcol=COLOR_YELLOW;
720 break;
721 case 34:
722 s->t_attrib.fgcol=COLOR_BLUE;
723 break;
724 case 35:
725 s->t_attrib.fgcol=COLOR_MAGENTA;
726 break;
727 case 36:
728 s->t_attrib.fgcol=COLOR_CYAN;
729 break;
730 case 37:
731 s->t_attrib.fgcol=COLOR_WHITE;
732 break;
733 /* set background color */
734 case 40:
735 s->t_attrib.bgcol=COLOR_BLACK;
736 break;
737 case 41:
738 s->t_attrib.bgcol=COLOR_RED;
739 break;
740 case 42:
741 s->t_attrib.bgcol=COLOR_GREEN;
742 break;
743 case 43:
744 s->t_attrib.bgcol=COLOR_YELLOW;
745 break;
746 case 44:
747 s->t_attrib.bgcol=COLOR_BLUE;
748 break;
749 case 45:
750 s->t_attrib.bgcol=COLOR_MAGENTA;
751 break;
752 case 46:
753 s->t_attrib.bgcol=COLOR_CYAN;
754 break;
755 case 47:
756 s->t_attrib.bgcol=COLOR_WHITE;
757 break;
758 }
759 }
760}
761
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200762static void console_clear_xy(QemuConsole *s, int x, int y)
thsadb47962007-01-16 23:02:36 +0000763{
764 int y1 = (s->y_base + y) % s->total_height;
765 TextCell *c = &s->cells[y1 * s->width + x];
766 c->ch = ' ';
767 c->t_attrib = s->t_attrib_default;
thsadb47962007-01-16 23:02:36 +0000768 update_xy(s, x, y);
769}
770
Ian Campbell3eea5492012-09-04 10:26:09 -0500771/* set cursor, checking bounds */
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200772static void set_cursor(QemuConsole *s, int x, int y)
Ian Campbell3eea5492012-09-04 10:26:09 -0500773{
774 if (x < 0) {
775 x = 0;
776 }
777 if (y < 0) {
778 y = 0;
779 }
780 if (y >= s->height) {
781 y = s->height - 1;
782 }
783 if (x >= s->width) {
784 x = s->width - 1;
785 }
786
787 s->x = x;
788 s->y = y;
789}
790
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200791static void console_putchar(QemuConsole *s, int ch)
bellarde7f0ad52004-07-14 17:28:59 +0000792{
793 TextCell *c;
thsadb47962007-01-16 23:02:36 +0000794 int y1, i;
795 int x, y;
bellarde7f0ad52004-07-14 17:28:59 +0000796
797 switch(s->state) {
798 case TTY_STATE_NORM:
799 switch(ch) {
pbrook6d6f7c22006-03-11 15:35:30 +0000800 case '\r': /* carriage return */
bellarde7f0ad52004-07-14 17:28:59 +0000801 s->x = 0;
802 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000803 case '\n': /* newline */
bellarde7f0ad52004-07-14 17:28:59 +0000804 console_put_lf(s);
805 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000806 case '\b': /* backspace */
ths5fafdf22007-09-16 21:08:06 +0000807 if (s->x > 0)
bellarde15d7372006-06-25 16:26:29 +0000808 s->x--;
pbrook6d6f7c22006-03-11 15:35:30 +0000809 break;
810 case '\t': /* tabspace */
811 if (s->x + (8 - (s->x % 8)) > s->width) {
bellardbd468842006-07-14 20:24:31 +0000812 s->x = 0;
pbrook6d6f7c22006-03-11 15:35:30 +0000813 console_put_lf(s);
814 } else {
815 s->x = s->x + (8 - (s->x % 8));
816 }
817 break;
818 case '\a': /* alert aka. bell */
819 /* TODO: has to be implemented */
820 break;
thsadb47962007-01-16 23:02:36 +0000821 case 14:
822 /* SI (shift in), character set 0 (ignored) */
823 break;
824 case 15:
825 /* SO (shift out), character set 1 (ignored) */
826 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000827 case 27: /* esc (introducing an escape sequence) */
bellarde7f0ad52004-07-14 17:28:59 +0000828 s->state = TTY_STATE_ESC;
829 break;
830 default:
thsed8276a2007-02-10 22:37:56 +0000831 if (s->x >= s->width) {
832 /* line wrap */
833 s->x = 0;
834 console_put_lf(s);
thsadb47962007-01-16 23:02:36 +0000835 }
bellarde7f0ad52004-07-14 17:28:59 +0000836 y1 = (s->y_base + s->y) % s->total_height;
837 c = &s->cells[y1 * s->width + s->x];
838 c->ch = ch;
pbrook6d6f7c22006-03-11 15:35:30 +0000839 c->t_attrib = s->t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +0000840 update_xy(s, s->x, s->y);
841 s->x++;
bellarde7f0ad52004-07-14 17:28:59 +0000842 break;
843 }
844 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000845 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
bellarde7f0ad52004-07-14 17:28:59 +0000846 if (ch == '[') {
847 for(i=0;i<MAX_ESC_PARAMS;i++)
848 s->esc_params[i] = 0;
849 s->nb_esc_params = 0;
850 s->state = TTY_STATE_CSI;
851 } else {
852 s->state = TTY_STATE_NORM;
853 }
854 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000855 case TTY_STATE_CSI: /* handle escape sequence parameters */
bellarde7f0ad52004-07-14 17:28:59 +0000856 if (ch >= '0' && ch <= '9') {
857 if (s->nb_esc_params < MAX_ESC_PARAMS) {
Laszlo Ersekc10600a2012-09-17 11:10:03 +0200858 int *param = &s->esc_params[s->nb_esc_params];
859 int digit = (ch - '0');
860
861 *param = (*param <= (INT_MAX - digit) / 10) ?
862 *param * 10 + digit : INT_MAX;
bellarde7f0ad52004-07-14 17:28:59 +0000863 }
864 } else {
Ian Campbell3eea5492012-09-04 10:26:09 -0500865 if (s->nb_esc_params < MAX_ESC_PARAMS)
866 s->nb_esc_params++;
bellarde7f0ad52004-07-14 17:28:59 +0000867 if (ch == ';')
868 break;
thsadb47962007-01-16 23:02:36 +0000869#ifdef DEBUG_CONSOLE
870 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
871 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
872#endif
bellarde7f0ad52004-07-14 17:28:59 +0000873 s->state = TTY_STATE_NORM;
874 switch(ch) {
thsadb47962007-01-16 23:02:36 +0000875 case 'A':
876 /* move cursor up */
877 if (s->esc_params[0] == 0) {
878 s->esc_params[0] = 1;
879 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500880 set_cursor(s, s->x, s->y - s->esc_params[0]);
bellarde7f0ad52004-07-14 17:28:59 +0000881 break;
thsadb47962007-01-16 23:02:36 +0000882 case 'B':
883 /* move cursor down */
884 if (s->esc_params[0] == 0) {
885 s->esc_params[0] = 1;
886 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500887 set_cursor(s, s->x, s->y + s->esc_params[0]);
thsadb47962007-01-16 23:02:36 +0000888 break;
889 case 'C':
890 /* move cursor right */
891 if (s->esc_params[0] == 0) {
892 s->esc_params[0] = 1;
893 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500894 set_cursor(s, s->x + s->esc_params[0], s->y);
thsadb47962007-01-16 23:02:36 +0000895 break;
896 case 'D':
897 /* move cursor left */
898 if (s->esc_params[0] == 0) {
899 s->esc_params[0] = 1;
900 }
Ian Campbell3eea5492012-09-04 10:26:09 -0500901 set_cursor(s, s->x - s->esc_params[0], s->y);
thsadb47962007-01-16 23:02:36 +0000902 break;
903 case 'G':
904 /* move cursor to column */
Ian Campbell3eea5492012-09-04 10:26:09 -0500905 set_cursor(s, s->esc_params[0] - 1, s->y);
thsadb47962007-01-16 23:02:36 +0000906 break;
907 case 'f':
908 case 'H':
909 /* move cursor to row, column */
Ian Campbell3eea5492012-09-04 10:26:09 -0500910 set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
thsadb47962007-01-16 23:02:36 +0000911 break;
912 case 'J':
913 switch (s->esc_params[0]) {
914 case 0:
915 /* clear to end of screen */
916 for (y = s->y; y < s->height; y++) {
917 for (x = 0; x < s->width; x++) {
918 if (y == s->y && x < s->x) {
919 continue;
920 }
921 console_clear_xy(s, x, y);
922 }
923 }
924 break;
925 case 1:
926 /* clear from beginning of screen */
927 for (y = 0; y <= s->y; y++) {
928 for (x = 0; x < s->width; x++) {
929 if (y == s->y && x > s->x) {
930 break;
931 }
932 console_clear_xy(s, x, y);
933 }
934 }
935 break;
936 case 2:
937 /* clear entire screen */
938 for (y = 0; y <= s->height; y++) {
939 for (x = 0; x < s->width; x++) {
940 console_clear_xy(s, x, y);
941 }
942 }
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100943 break;
thsadb47962007-01-16 23:02:36 +0000944 }
Markus Armbruster95d8f9f2011-11-22 11:59:07 +0100945 break;
thsadb47962007-01-16 23:02:36 +0000946 case 'K':
947 switch (s->esc_params[0]) {
948 case 0:
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100949 /* clear to eol */
950 for(x = s->x; x < s->width; x++) {
thsadb47962007-01-16 23:02:36 +0000951 console_clear_xy(s, x, s->y);
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100952 }
953 break;
thsadb47962007-01-16 23:02:36 +0000954 case 1:
955 /* clear from beginning of line */
956 for (x = 0; x <= s->x; x++) {
957 console_clear_xy(s, x, s->y);
958 }
959 break;
960 case 2:
961 /* clear entire line */
962 for(x = 0; x < s->width; x++) {
963 console_clear_xy(s, x, s->y);
964 }
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100965 break;
966 }
thsadb47962007-01-16 23:02:36 +0000967 break;
968 case 'm':
Markus Armbrusterf94a9502011-11-22 11:59:06 +0100969 console_handle_escape(s);
970 break;
thsadb47962007-01-16 23:02:36 +0000971 case 'n':
972 /* report cursor position */
973 /* TODO: send ESC[row;colR */
974 break;
975 case 's':
976 /* save cursor position */
977 s->x_saved = s->x;
978 s->y_saved = s->y;
979 break;
980 case 'u':
981 /* restore cursor position */
982 s->x = s->x_saved;
983 s->y = s->y_saved;
984 break;
985 default:
986#ifdef DEBUG_CONSOLE
987 fprintf(stderr, "unhandled escape character '%c'\n", ch);
988#endif
989 break;
990 }
991 break;
bellarde7f0ad52004-07-14 17:28:59 +0000992 }
993 }
994}
995
996void console_select(unsigned int index)
997{
Gerd Hoffmann284d1c62013-03-15 15:45:54 +0100998 DisplayChangeListener *dcl;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +0200999 QemuConsole *s;
pbrook6d6f7c22006-03-11 15:35:30 +00001000
bellarde7f0ad52004-07-14 17:28:59 +00001001 if (index >= MAX_CONSOLES)
1002 return;
Gerd Hoffmann437fe102013-03-07 16:04:52 +01001003
1004 trace_console_select(index);
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001005 s = qemu_console_lookup_by_index(index);
bellarde7f0ad52004-07-14 17:28:59 +00001006 if (s) {
aliguori7d957bd2009-01-15 22:14:11 +00001007 DisplayState *ds = s->ds;
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001008
Stefan Weil8bd6b062012-08-17 15:50:44 +02001009 if (active_console && active_console->cursor_timer) {
Alex Blighbc72ad62013-08-21 16:03:08 +01001010 timer_del(active_console->cursor_timer);
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001011 }
bellarde7f0ad52004-07-14 17:28:59 +00001012 active_console = s;
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +02001013 if (ds->have_gfx) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001014 QLIST_FOREACH(dcl, &ds->listeners, next) {
1015 if (dcl->con != NULL) {
1016 continue;
1017 }
1018 if (dcl->ops->dpy_gfx_switch) {
1019 dcl->ops->dpy_gfx_switch(dcl, s->surface);
1020 }
1021 }
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001022 dpy_gfx_update(s, 0, 0, surface_width(s->surface),
1023 surface_height(s->surface));
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +02001024 }
1025 if (ds->have_text) {
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001026 dpy_text_resize(s, s->width, s->height);
aliguori68f00992009-01-21 18:59:12 +00001027 }
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001028 if (s->cursor_timer) {
Alex Blighbc72ad62013-08-21 16:03:08 +01001029 timer_mod(s->cursor_timer,
1030 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001031 }
bellarde7f0ad52004-07-14 17:28:59 +00001032 }
1033}
1034
1035static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1036{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001037 QemuConsole *s = chr->opaque;
bellarde7f0ad52004-07-14 17:28:59 +00001038 int i;
1039
pbrook14778c22009-01-21 03:02:52 +00001040 s->update_x0 = s->width * FONT_WIDTH;
1041 s->update_y0 = s->height * FONT_HEIGHT;
1042 s->update_x1 = 0;
1043 s->update_y1 = 0;
bellarde7f0ad52004-07-14 17:28:59 +00001044 console_show_cursor(s, 0);
1045 for(i = 0; i < len; i++) {
1046 console_putchar(s, buf[i]);
1047 }
1048 console_show_cursor(s, 1);
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +02001049 if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001050 dpy_gfx_update(s, s->update_x0, s->update_y0,
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +02001051 s->update_x1 - s->update_x0,
1052 s->update_y1 - s->update_y0);
pbrook14778c22009-01-21 03:02:52 +00001053 }
bellarde7f0ad52004-07-14 17:28:59 +00001054 return len;
1055}
1056
bellarde15d7372006-06-25 16:26:29 +00001057static void kbd_send_chars(void *opaque)
1058{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001059 QemuConsole *s = opaque;
bellarde15d7372006-06-25 16:26:29 +00001060 int len;
1061 uint8_t buf[16];
ths3b46e622007-09-17 08:09:54 +00001062
Anthony Liguori909cda12011-08-15 11:17:31 -05001063 len = qemu_chr_be_can_write(s->chr);
bellarde15d7372006-06-25 16:26:29 +00001064 if (len > s->out_fifo.count)
1065 len = s->out_fifo.count;
1066 if (len > 0) {
1067 if (len > sizeof(buf))
1068 len = sizeof(buf);
1069 qemu_fifo_read(&s->out_fifo, buf, len);
Anthony Liguorifa5efcc2011-08-15 11:17:30 -05001070 qemu_chr_be_write(s->chr, buf, len);
bellarde15d7372006-06-25 16:26:29 +00001071 }
1072 /* characters are pending: we send them a bit later (XXX:
1073 horrible, should change char device API) */
1074 if (s->out_fifo.count > 0) {
Alex Blighbc72ad62013-08-21 16:03:08 +01001075 timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
bellarde15d7372006-06-25 16:26:29 +00001076 }
1077}
1078
bellarde7f0ad52004-07-14 17:28:59 +00001079/* called when an ascii key is pressed */
1080void kbd_put_keysym(int keysym)
1081{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001082 QemuConsole *s;
bellarde7f0ad52004-07-14 17:28:59 +00001083 uint8_t buf[16], *q;
1084 int c;
1085
1086 s = active_console;
thsaf3a9032007-07-11 23:14:59 +00001087 if (!s || (s->console_type == GRAPHIC_CONSOLE))
bellarde7f0ad52004-07-14 17:28:59 +00001088 return;
1089
1090 switch(keysym) {
1091 case QEMU_KEY_CTRL_UP:
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001092 console_scroll(s, -1);
bellarde7f0ad52004-07-14 17:28:59 +00001093 break;
1094 case QEMU_KEY_CTRL_DOWN:
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001095 console_scroll(s, 1);
bellarde7f0ad52004-07-14 17:28:59 +00001096 break;
1097 case QEMU_KEY_CTRL_PAGEUP:
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001098 console_scroll(s, -10);
bellarde7f0ad52004-07-14 17:28:59 +00001099 break;
1100 case QEMU_KEY_CTRL_PAGEDOWN:
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001101 console_scroll(s, 10);
bellarde7f0ad52004-07-14 17:28:59 +00001102 break;
1103 default:
bellarde15d7372006-06-25 16:26:29 +00001104 /* convert the QEMU keysym to VT100 key string */
1105 q = buf;
1106 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1107 *q++ = '\033';
1108 *q++ = '[';
1109 c = keysym - 0xe100;
1110 if (c >= 10)
1111 *q++ = '0' + (c / 10);
1112 *q++ = '0' + (c % 10);
1113 *q++ = '~';
1114 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1115 *q++ = '\033';
1116 *q++ = '[';
1117 *q++ = keysym & 0xff;
Paolo Bonzini41048332010-12-23 13:42:52 +01001118 } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
1119 console_puts(s->chr, (const uint8_t *) "\r", 1);
1120 *q++ = '\n';
bellarde15d7372006-06-25 16:26:29 +00001121 } else {
Paolo Bonzini41048332010-12-23 13:42:52 +01001122 *q++ = keysym;
1123 }
1124 if (s->echo) {
1125 console_puts(s->chr, buf, q - buf);
bellarde15d7372006-06-25 16:26:29 +00001126 }
pbrooke5b0bc42007-01-27 23:46:43 +00001127 if (s->chr->chr_read) {
bellarde15d7372006-06-25 16:26:29 +00001128 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1129 kbd_send_chars(s);
bellarde7f0ad52004-07-14 17:28:59 +00001130 }
1131 break;
1132 }
1133}
1134
balrog4d3b6f62008-02-10 16:33:14 +00001135static void text_console_invalidate(void *opaque)
1136{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001137 QemuConsole *s = (QemuConsole *) opaque;
Gerd Hoffmann1562e532013-03-06 13:40:47 +01001138
1139 if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
aliguori68f00992009-01-21 18:59:12 +00001140 text_console_resize(s);
1141 }
balrog4d3b6f62008-02-10 16:33:14 +00001142 console_refresh(s);
1143}
1144
Anthony Liguoric227f092009-10-01 16:12:16 -05001145static void text_console_update(void *opaque, console_ch_t *chardata)
balrog4d3b6f62008-02-10 16:33:14 +00001146{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001147 QemuConsole *s = (QemuConsole *) opaque;
balrog4d3b6f62008-02-10 16:33:14 +00001148 int i, j, src;
1149
1150 if (s->text_x[0] <= s->text_x[1]) {
1151 src = (s->y_base + s->text_y[0]) * s->width;
1152 chardata += s->text_y[0] * s->width;
1153 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1154 for (j = 0; j < s->width; j ++, src ++)
1155 console_write_ch(chardata ++, s->cells[src].ch |
1156 (s->cells[src].t_attrib.fgcol << 12) |
1157 (s->cells[src].t_attrib.bgcol << 8) |
1158 (s->cells[src].t_attrib.bold << 21));
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001159 dpy_text_update(s, s->text_x[0], s->text_y[0],
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +02001160 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
balrog4d3b6f62008-02-10 16:33:14 +00001161 s->text_x[0] = s->width;
1162 s->text_y[0] = s->height;
1163 s->text_x[1] = 0;
1164 s->text_y[1] = 0;
1165 }
1166 if (s->cursor_invalidate) {
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001167 dpy_text_cursor(s, s->x, s->y);
balrog4d3b6f62008-02-10 16:33:14 +00001168 s->cursor_invalidate = 0;
1169 }
1170}
1171
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001172static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
bellarde7f0ad52004-07-14 17:28:59 +00001173{
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +02001174 Error *local_err = NULL;
Gerd Hoffmann95be0662013-04-17 09:45:10 +02001175 Object *obj;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001176 QemuConsole *s;
pbrook95219892006-04-09 01:06:34 +00001177 int i;
bellarde7f0ad52004-07-14 17:28:59 +00001178
1179 if (nb_consoles >= MAX_CONSOLES)
1180 return NULL;
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +02001181
Gerd Hoffmann95be0662013-04-17 09:45:10 +02001182 obj = object_new(TYPE_QEMU_CONSOLE);
1183 s = QEMU_CONSOLE(obj);
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +02001184 object_property_add_link(obj, "device", TYPE_DEVICE,
1185 (Object **)&s->device, &local_err);
1186
thsaf3a9032007-07-11 23:14:59 +00001187 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1188 (console_type == GRAPHIC_CONSOLE))) {
bellarde7f0ad52004-07-14 17:28:59 +00001189 active_console = s;
thsaf3a9032007-07-11 23:14:59 +00001190 }
bellarde7f0ad52004-07-14 17:28:59 +00001191 s->ds = ds;
thsaf3a9032007-07-11 23:14:59 +00001192 s->console_type = console_type;
1193 if (console_type != GRAPHIC_CONSOLE) {
Jan Kiszkaf81bdef2011-09-16 00:48:07 +02001194 s->index = nb_consoles;
pbrook95219892006-04-09 01:06:34 +00001195 consoles[nb_consoles++] = s;
1196 } else {
1197 /* HACK: Put graphical consoles before text consoles. */
1198 for (i = nb_consoles; i > 0; i--) {
thsaf3a9032007-07-11 23:14:59 +00001199 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
pbrook95219892006-04-09 01:06:34 +00001200 break;
1201 consoles[i] = consoles[i - 1];
Jan Kiszkaf81bdef2011-09-16 00:48:07 +02001202 consoles[i]->index = i;
pbrook95219892006-04-09 01:06:34 +00001203 }
Jan Kiszkaf81bdef2011-09-16 00:48:07 +02001204 s->index = i;
pbrook95219892006-04-09 01:06:34 +00001205 consoles[i] = s;
aliguori3023f332009-01-16 19:04:14 +00001206 nb_consoles++;
pbrook95219892006-04-09 01:06:34 +00001207 }
bellarde7f0ad52004-07-14 17:28:59 +00001208 return s;
1209}
1210
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001211static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
1212 int linesize, PixelFormat pf, int newflags)
Jes Sorensenffe8b822011-03-16 13:33:30 +01001213{
Jes Sorensenffe8b822011-03-16 13:33:30 +01001214 surface->pf = pf;
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001215
1216 qemu_pixman_image_unref(surface->image);
1217 surface->image = NULL;
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001218
1219 surface->format = qemu_pixman_get_format(&pf);
1220 assert(surface->format != 0);
1221 surface->image = pixman_image_create_bits(surface->format,
1222 width, height,
1223 NULL, linesize);
1224 assert(surface->image != NULL);
1225
Jes Sorensenffe8b822011-03-16 13:33:30 +01001226 surface->flags = newflags | QEMU_ALLOCATED_FLAG;
Paolo Bonzini98b50082010-02-11 00:29:57 +01001227#ifdef HOST_WORDS_BIGENDIAN
Jes Sorensenffe8b822011-03-16 13:33:30 +01001228 surface->flags |= QEMU_BIG_ENDIAN_FLAG;
Paolo Bonzini98b50082010-02-11 00:29:57 +01001229#endif
Paolo Bonzini98b50082010-02-11 00:29:57 +01001230}
1231
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001232DisplaySurface *qemu_create_displaysurface(int width, int height)
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001233{
1234 DisplaySurface *surface = g_new0(DisplaySurface, 1);
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001235 int linesize = width * 4;
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001236
1237 trace_displaysurface_create(surface, width, height);
Gerd Hoffmann537a4392012-09-27 11:06:36 +02001238 qemu_alloc_display(surface, width, height, linesize,
1239 qemu_default_pixelformat(32), 0);
1240 return surface;
1241}
1242
Gerd Hoffmann187cd1d2012-09-26 07:46:20 +02001243DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
Gerd Hoffmannb1424e02013-02-20 09:37:12 +01001244 int linesize, uint8_t *data,
1245 bool byteswap)
Paolo Bonzini98b50082010-02-11 00:29:57 +01001246{
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001247 DisplaySurface *surface = g_new0(DisplaySurface, 1);
Paolo Bonzini98b50082010-02-11 00:29:57 +01001248
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001249 trace_displaysurface_create_from(surface, width, height, bpp, byteswap);
Gerd Hoffmannb1424e02013-02-20 09:37:12 +01001250 if (byteswap) {
1251 surface->pf = qemu_different_endianness_pixelformat(bpp);
1252 } else {
1253 surface->pf = qemu_default_pixelformat(bpp);
1254 }
Gerd Hoffmann69c77772012-09-26 15:20:05 +02001255
1256 surface->format = qemu_pixman_get_format(&surface->pf);
1257 assert(surface->format != 0);
1258 surface->image = pixman_image_create_bits(surface->format,
1259 width, height,
1260 (void *)data, linesize);
1261 assert(surface->image != NULL);
1262
Paolo Bonzini98b50082010-02-11 00:29:57 +01001263#ifdef HOST_WORDS_BIGENDIAN
1264 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1265#endif
Paolo Bonzini98b50082010-02-11 00:29:57 +01001266
1267 return surface;
1268}
1269
Gerd Hoffmannd3002b02013-04-25 09:33:19 +02001270static DisplaySurface *qemu_create_dummy_surface(void)
1271{
1272 static const char msg[] =
1273 "This VM has no graphic display device.";
1274 DisplaySurface *surface = qemu_create_displaysurface(640, 480);
1275 pixman_color_t bg = color_table_rgb[0][COLOR_BLACK];
1276 pixman_color_t fg = color_table_rgb[0][COLOR_WHITE];
1277 pixman_image_t *glyph;
1278 int len, x, y, i;
1279
1280 len = strlen(msg);
1281 x = (640/FONT_WIDTH - len) / 2;
1282 y = (480/FONT_HEIGHT - 1) / 2;
1283 for (i = 0; i < len; i++) {
1284 glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
1285 qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
1286 x+i, y, FONT_WIDTH, FONT_HEIGHT);
1287 qemu_pixman_image_unref(glyph);
1288 }
1289 return surface;
1290}
1291
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001292void qemu_free_displaysurface(DisplaySurface *surface)
Paolo Bonzini98b50082010-02-11 00:29:57 +01001293{
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001294 if (surface == NULL) {
Paolo Bonzini98b50082010-02-11 00:29:57 +01001295 return;
Gerd Hoffmann187cd1d2012-09-26 07:46:20 +02001296 }
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001297 trace_displaysurface_free(surface);
1298 qemu_pixman_image_unref(surface->image);
1299 g_free(surface);
Paolo Bonzini98b50082010-02-11 00:29:57 +01001300}
1301
Gerd Hoffmann52090892013-04-23 15:44:31 +02001302void register_displaychangelistener(DisplayChangeListener *dcl)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001303{
Gerd Hoffmannd3002b02013-04-25 09:33:19 +02001304 static DisplaySurface *dummy;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001305 QemuConsole *con;
1306
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001307 trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
Gerd Hoffmann52090892013-04-23 15:44:31 +02001308 dcl->ds = get_alloc_displaystate();
1309 QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
1310 gui_setup_refresh(dcl->ds);
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001311 if (dcl->con) {
1312 dcl->con->dcls++;
1313 con = dcl->con;
1314 } else {
1315 con = active_console;
1316 }
Gerd Hoffmannd3002b02013-04-25 09:33:19 +02001317 if (dcl->ops->dpy_gfx_switch) {
1318 if (con) {
1319 dcl->ops->dpy_gfx_switch(dcl, con->surface);
1320 } else {
1321 if (!dummy) {
1322 dummy = qemu_create_dummy_surface();
1323 }
1324 dcl->ops->dpy_gfx_switch(dcl, dummy);
1325 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001326 }
1327}
1328
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01001329void update_displaychangelistener(DisplayChangeListener *dcl,
1330 uint64_t interval)
1331{
1332 DisplayState *ds = dcl->ds;
1333
1334 dcl->update_interval = interval;
1335 if (!ds->refreshing && ds->update_interval > interval) {
Alex Blighbc72ad62013-08-21 16:03:08 +01001336 timer_mod(ds->gui_timer, ds->last_update + interval);
Gerd Hoffmann0f7b2862013-03-14 11:56:16 +01001337 }
1338}
1339
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001340void unregister_displaychangelistener(DisplayChangeListener *dcl)
1341{
1342 DisplayState *ds = dcl->ds;
1343 trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001344 if (dcl->con) {
1345 dcl->con->dcls--;
1346 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001347 QLIST_REMOVE(dcl, next);
1348 gui_setup_refresh(ds);
1349}
1350
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001351void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001352{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001353 DisplayState *s = con->ds;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001354 DisplayChangeListener *dcl;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001355 int width = surface_width(con->surface);
1356 int height = surface_height(con->surface);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001357
1358 x = MAX(x, 0);
1359 y = MAX(y, 0);
1360 x = MIN(x, width);
1361 y = MIN(y, height);
1362 w = MIN(w, width - x);
1363 h = MIN(h, height - y);
1364
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001365 if (!qemu_console_is_visible(con)) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001366 return;
1367 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001368 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001369 if (con != (dcl->con ? dcl->con : active_console)) {
1370 continue;
1371 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001372 if (dcl->ops->dpy_gfx_update) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001373 dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001374 }
1375 }
1376}
1377
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001378void dpy_gfx_replace_surface(QemuConsole *con,
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001379 DisplaySurface *surface)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001380{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001381 DisplayState *s = con->ds;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001382 DisplaySurface *old_surface = con->surface;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001383 DisplayChangeListener *dcl;
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001384
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001385 con->surface = surface;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001386 QLIST_FOREACH(dcl, &s->listeners, next) {
1387 if (con != (dcl->con ? dcl->con : active_console)) {
1388 continue;
1389 }
1390 if (dcl->ops->dpy_gfx_switch) {
1391 dcl->ops->dpy_gfx_switch(dcl, surface);
1392 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001393 }
Gerd Hoffmannda229ef2013-02-28 10:48:02 +01001394 qemu_free_displaysurface(old_surface);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001395}
1396
1397void dpy_refresh(DisplayState *s)
1398{
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001399 DisplayChangeListener *dcl;
1400
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001401 QLIST_FOREACH(dcl, &s->listeners, next) {
1402 if (dcl->ops->dpy_refresh) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001403 dcl->ops->dpy_refresh(dcl);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001404 }
1405 }
1406}
1407
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001408void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
1409 int dst_x, int dst_y, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001410{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001411 DisplayState *s = con->ds;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001412 DisplayChangeListener *dcl;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001413
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001414 if (!qemu_console_is_visible(con)) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001415 return;
1416 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001417 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001418 if (con != (dcl->con ? dcl->con : active_console)) {
1419 continue;
1420 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001421 if (dcl->ops->dpy_gfx_copy) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001422 dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001423 } else { /* TODO */
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001424 dcl->ops->dpy_gfx_update(dcl, dst_x, dst_y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001425 }
1426 }
1427}
1428
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001429void dpy_text_cursor(QemuConsole *con, int x, int y)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001430{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001431 DisplayState *s = con->ds;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001432 DisplayChangeListener *dcl;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001433
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001434 if (!qemu_console_is_visible(con)) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001435 return;
1436 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001437 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001438 if (con != (dcl->con ? dcl->con : active_console)) {
1439 continue;
1440 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001441 if (dcl->ops->dpy_text_cursor) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001442 dcl->ops->dpy_text_cursor(dcl, x, y);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001443 }
1444 }
1445}
1446
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001447void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001448{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001449 DisplayState *s = con->ds;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001450 DisplayChangeListener *dcl;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001451
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001452 if (!qemu_console_is_visible(con)) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001453 return;
1454 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001455 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001456 if (con != (dcl->con ? dcl->con : active_console)) {
1457 continue;
1458 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001459 if (dcl->ops->dpy_text_update) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001460 dcl->ops->dpy_text_update(dcl, x, y, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001461 }
1462 }
1463}
1464
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001465void dpy_text_resize(QemuConsole *con, int w, int h)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001466{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001467 DisplayState *s = con->ds;
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001468 struct DisplayChangeListener *dcl;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001469
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001470 if (!qemu_console_is_visible(con)) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001471 return;
1472 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001473 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001474 if (con != (dcl->con ? dcl->con : active_console)) {
1475 continue;
1476 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001477 if (dcl->ops->dpy_text_resize) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001478 dcl->ops->dpy_text_resize(dcl, w, h);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001479 }
1480 }
1481}
1482
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001483void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001484{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001485 DisplayState *s = con->ds;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001486 DisplayChangeListener *dcl;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001487
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001488 if (!qemu_console_is_visible(con)) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001489 return;
1490 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001491 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001492 if (con != (dcl->con ? dcl->con : active_console)) {
1493 continue;
1494 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001495 if (dcl->ops->dpy_mouse_set) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001496 dcl->ops->dpy_mouse_set(dcl, x, y, on);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001497 }
1498 }
1499}
1500
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001501void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001502{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001503 DisplayState *s = con->ds;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001504 DisplayChangeListener *dcl;
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001505
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001506 if (!qemu_console_is_visible(con)) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001507 return;
1508 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001509 QLIST_FOREACH(dcl, &s->listeners, next) {
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001510 if (con != (dcl->con ? dcl->con : active_console)) {
1511 continue;
1512 }
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001513 if (dcl->ops->dpy_cursor_define) {
Gerd Hoffmannbc2ed972013-03-01 13:03:04 +01001514 dcl->ops->dpy_cursor_define(dcl, cursor);
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001515 }
1516 }
1517}
1518
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001519bool dpy_cursor_define_supported(QemuConsole *con)
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001520{
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001521 DisplayState *s = con->ds;
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001522 DisplayChangeListener *dcl;
1523
Gerd Hoffmann7c20b4a2012-11-13 14:51:41 +01001524 QLIST_FOREACH(dcl, &s->listeners, next) {
1525 if (dcl->ops->dpy_cursor_define) {
1526 return true;
1527 }
1528 }
1529 return false;
1530}
1531
Paolo Bonzini98b50082010-02-11 00:29:57 +01001532/***********************************************************/
1533/* register display */
1534
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001535/* console.c internal use only */
1536static DisplayState *get_alloc_displaystate(void)
Paolo Bonzini98b50082010-02-11 00:29:57 +01001537{
1538 if (!display_state) {
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001539 display_state = g_new0(DisplayState, 1);
Paolo Bonzini98b50082010-02-11 00:29:57 +01001540 }
1541 return display_state;
1542}
1543
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001544/*
1545 * Called by main(), after creating QemuConsoles
1546 * and before initializing ui (sdl/vnc/...).
1547 */
1548DisplayState *init_displaystate(void)
1549{
Gerd Hoffmann43f420f2013-06-25 10:49:31 +02001550 Error *local_err = NULL;
1551 gchar *name;
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001552 int i;
1553
1554 if (!display_state) {
1555 display_state = g_new0(DisplayState, 1);
1556 }
1557
1558 for (i = 0; i < nb_consoles; i++) {
1559 if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
1560 consoles[i]->ds == NULL) {
1561 text_console_do_init(consoles[i]->chr, display_state);
1562 }
Gerd Hoffmann43f420f2013-06-25 10:49:31 +02001563
1564 /* Hook up into the qom tree here (not in new_console()), once
1565 * all QemuConsoles are created and the order / numbering
1566 * doesn't change any more */
1567 name = g_strdup_printf("console[%d]", i);
1568 object_property_add_child(container_get(object_get_root(), "/backend"),
1569 name, OBJECT(consoles[i]), &local_err);
1570 g_free(name);
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001571 }
1572
1573 return display_state;
1574}
1575
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +02001576QemuConsole *graphic_console_init(DeviceState *dev,
1577 const GraphicHwOps *hw_ops,
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001578 void *opaque)
bellarde7f0ad52004-07-14 17:28:59 +00001579{
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +02001580 Error *local_err = NULL;
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001581 int width = 640;
1582 int height = 480;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001583 QemuConsole *s;
aliguori3023f332009-01-16 19:04:14 +00001584 DisplayState *ds;
aurel32f0f2f972009-01-16 21:13:49 +00001585
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001586 ds = get_alloc_displaystate();
Gerd Hoffmann437fe102013-03-07 16:04:52 +01001587 trace_console_gfx_new();
thsaf3a9032007-07-11 23:14:59 +00001588 s = new_console(ds, GRAPHIC_CONSOLE);
Gerd Hoffmann380cd052013-03-13 14:04:18 +01001589 s->hw_ops = hw_ops;
pbrook95219892006-04-09 01:06:34 +00001590 s->hw = opaque;
Gerd Hoffmannaa2beaa2013-04-17 10:21:27 +02001591 if (dev) {
1592 object_property_set_link(OBJECT(s), OBJECT(dev),
1593 "device", &local_err);
1594 }
aliguori3023f332009-01-16 19:04:14 +00001595
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001596 s->surface = qemu_create_displaysurface(width, height);
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001597 return s;
pbrook95219892006-04-09 01:06:34 +00001598}
1599
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001600QemuConsole *qemu_console_lookup_by_index(unsigned int index)
1601{
1602 if (index >= MAX_CONSOLES) {
1603 return NULL;
1604 }
1605 return consoles[index];
1606}
1607
Gerd Hoffmann14a93642013-04-18 07:30:40 +02001608QemuConsole *qemu_console_lookup_by_device(DeviceState *dev)
1609{
1610 Error *local_err = NULL;
1611 Object *obj;
1612 int i;
1613
1614 for (i = 0; i < nb_consoles; i++) {
1615 if (!consoles[i]) {
1616 continue;
1617 }
1618 obj = object_property_get_link(OBJECT(consoles[i]),
1619 "device", &local_err);
1620 if (DEVICE(obj) == dev) {
1621 return consoles[i];
1622 }
1623 }
1624 return NULL;
1625}
1626
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001627bool qemu_console_is_visible(QemuConsole *con)
pbrook95219892006-04-09 01:06:34 +00001628{
Gerd Hoffmann284d1c62013-03-15 15:45:54 +01001629 return (con == active_console) || (con->dcls > 0);
bellarde7f0ad52004-07-14 17:28:59 +00001630}
1631
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001632bool qemu_console_is_graphic(QemuConsole *con)
balrogc21bbcf2008-09-24 03:32:33 +00001633{
Gerd Hoffmann81c0d5a2013-03-14 14:27:08 +01001634 if (con == NULL) {
1635 con = active_console;
1636 }
1637 return con && (con->console_type == GRAPHIC_CONSOLE);
1638}
1639
1640bool qemu_console_is_fixedsize(QemuConsole *con)
1641{
1642 if (con == NULL) {
1643 con = active_console;
1644 }
1645 return con && (con->console_type != TEXT_CONSOLE);
balrogc21bbcf2008-09-24 03:32:33 +00001646}
1647
Paolo Bonzini41048332010-12-23 13:42:52 +01001648static void text_console_set_echo(CharDriverState *chr, bool echo)
1649{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001650 QemuConsole *s = chr->opaque;
Paolo Bonzini41048332010-12-23 13:42:52 +01001651
1652 s->echo = echo;
1653}
1654
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001655static void text_console_update_cursor(void *opaque)
1656{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001657 QemuConsole *s = opaque;
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001658
1659 s->cursor_visible_phase = !s->cursor_visible_phase;
Gerd Hoffmann1dbfa002013-03-12 13:44:38 +01001660 graphic_hw_invalidate(s);
Alex Blighbc72ad62013-08-21 16:03:08 +01001661 timer_mod(s->cursor_timer,
1662 qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001663}
1664
Gerd Hoffmann380cd052013-03-13 14:04:18 +01001665static const GraphicHwOps text_console_ops = {
1666 .invalidate = text_console_invalidate,
1667 .text_update = text_console_update,
1668};
1669
Paolo Bonzini44b37b92010-12-23 13:42:53 +01001670static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
bellarde7f0ad52004-07-14 17:28:59 +00001671{
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001672 QemuConsole *s;
Gerd Hoffmann36671fb2013-03-13 10:14:52 +01001673 int g_width = 80 * FONT_WIDTH;
1674 int g_height = 24 * FONT_HEIGHT;
pbrook6d6f7c22006-03-11 15:35:30 +00001675
Paolo Bonzini491e1142010-12-23 13:42:51 +01001676 s = chr->opaque;
Gerd Hoffmann6ea314d2009-09-10 10:58:49 +02001677
bellarde7f0ad52004-07-14 17:28:59 +00001678 chr->chr_write = console_puts;
bellard6fcfafb2004-08-01 21:48:30 +00001679
bellarde15d7372006-06-25 16:26:29 +00001680 s->out_fifo.buf = s->out_fifo_buf;
1681 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
Alex Blighbc72ad62013-08-21 16:03:08 +01001682 s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
aliguori3023f332009-01-16 19:04:14 +00001683 s->ds = ds;
ths3b46e622007-09-17 08:09:54 +00001684
bellarde7f0ad52004-07-14 17:28:59 +00001685 s->y_displayed = 0;
1686 s->y_base = 0;
1687 s->total_height = DEFAULT_BACKSCROLL;
1688 s->x = 0;
1689 s->y = 0;
Gerd Hoffmann36671fb2013-03-13 10:14:52 +01001690 if (!s->surface) {
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001691 if (active_console && active_console->surface) {
Gerd Hoffmann36671fb2013-03-13 10:14:52 +01001692 g_width = surface_width(active_console->surface);
1693 g_height = surface_height(active_console->surface);
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001694 }
Gerd Hoffmann36671fb2013-03-13 10:14:52 +01001695 s->surface = qemu_create_displaysurface(g_width, g_height);
Paolo Bonzini491e1142010-12-23 13:42:51 +01001696 }
pbrook6d6f7c22006-03-11 15:35:30 +00001697
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001698 s->cursor_timer =
Alex Blighbc72ad62013-08-21 16:03:08 +01001699 timer_new_ms(QEMU_CLOCK_REALTIME, text_console_update_cursor, s);
Jan Kiszkabf1bed82012-07-10 22:00:55 +02001700
Gerd Hoffmann380cd052013-03-13 14:04:18 +01001701 s->hw_ops = &text_console_ops;
balrog4d3b6f62008-02-10 16:33:14 +00001702 s->hw = s;
1703
pbrook6d6f7c22006-03-11 15:35:30 +00001704 /* Set text attribute defaults */
1705 s->t_attrib_default.bold = 0;
1706 s->t_attrib_default.uline = 0;
1707 s->t_attrib_default.blink = 0;
1708 s->t_attrib_default.invers = 0;
1709 s->t_attrib_default.unvisible = 0;
1710 s->t_attrib_default.fgcol = COLOR_WHITE;
1711 s->t_attrib_default.bgcol = COLOR_BLACK;
pbrook6d6f7c22006-03-11 15:35:30 +00001712 /* set current text attributes to default */
1713 s->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +00001714 text_console_resize(s);
1715
Gerd Hoffmann51bfa4d2009-12-08 13:11:39 +01001716 if (chr->label) {
1717 char msg[128];
1718 int len;
1719
Gerd Hoffmann735ba582009-12-08 13:11:40 +01001720 s->t_attrib.bgcol = COLOR_BLUE;
Gerd Hoffmann51bfa4d2009-12-08 13:11:39 +01001721 len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
1722 console_puts(chr, (uint8_t*)msg, len);
Gerd Hoffmann735ba582009-12-08 13:11:40 +01001723 s->t_attrib = s->t_attrib_default;
Gerd Hoffmann51bfa4d2009-12-08 13:11:39 +01001724 }
1725
Hans de Goedefee204f2013-03-26 11:07:54 +01001726 qemu_chr_be_generic_open(chr);
aurel32ceecf1d2009-01-18 14:08:04 +00001727 if (chr->init)
1728 chr->init(chr);
bellarde7f0ad52004-07-14 17:28:59 +00001729}
pbrookc60e08d2008-07-01 16:24:38 +00001730
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001731static CharDriverState *text_console_init(ChardevVC *vc)
aliguori2796dae2009-01-16 20:23:27 +00001732{
1733 CharDriverState *chr;
Gerd Hoffmann76ffb0b2012-09-28 13:24:17 +02001734 QemuConsole *s;
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001735 unsigned width = 0;
1736 unsigned height = 0;
aliguori2796dae2009-01-16 20:23:27 +00001737
Anthony Liguori7267c092011-08-20 22:09:37 -05001738 chr = g_malloc0(sizeof(CharDriverState));
aliguori2796dae2009-01-16 20:23:27 +00001739
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001740 if (vc->has_width) {
1741 width = vc->width;
1742 } else if (vc->has_cols) {
1743 width = vc->cols * FONT_WIDTH;
1744 }
Paolo Bonzini491e1142010-12-23 13:42:51 +01001745
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001746 if (vc->has_height) {
1747 height = vc->height;
1748 } else if (vc->has_rows) {
1749 height = vc->rows * FONT_HEIGHT;
1750 }
Paolo Bonzini491e1142010-12-23 13:42:51 +01001751
Gerd Hoffmann437fe102013-03-07 16:04:52 +01001752 trace_console_txt_new(width, height);
Paolo Bonzini491e1142010-12-23 13:42:51 +01001753 if (width == 0 || height == 0) {
1754 s = new_console(NULL, TEXT_CONSOLE);
1755 } else {
1756 s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
Gerd Hoffmann36671fb2013-03-13 10:14:52 +01001757 s->surface = qemu_create_displaysurface(width, height);
Paolo Bonzini491e1142010-12-23 13:42:51 +01001758 }
1759
1760 if (!s) {
Stefan Weil5354d082011-10-02 18:53:09 +02001761 g_free(chr);
Markus Armbruster1f514702012-02-07 15:09:08 +01001762 return NULL;
Paolo Bonzini491e1142010-12-23 13:42:51 +01001763 }
1764
1765 s->chr = chr;
Paolo Bonzini491e1142010-12-23 13:42:51 +01001766 chr->opaque = s;
Paolo Bonzini41048332010-12-23 13:42:52 +01001767 chr->chr_set_echo = text_console_set_echo;
Michael Rothbd5c51e2013-06-07 15:19:53 -05001768 /* console/chardev init sometimes completes elsewhere in a 2nd
1769 * stage, so defer OPENED events until they are fully initialized
1770 */
1771 chr->explicit_be_open = true;
Gerd Hoffmann64840c62013-03-07 17:08:29 +01001772
1773 if (display_state) {
1774 text_console_do_init(chr, display_state);
1775 }
Markus Armbruster1f514702012-02-07 15:09:08 +01001776 return chr;
aliguori2796dae2009-01-16 20:23:27 +00001777}
1778
Anthony Liguorid82831d2013-02-20 07:43:19 -06001779static VcHandler *vc_handler = text_console_init;
1780
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001781CharDriverState *vc_init(ChardevVC *vc)
Anthony Liguorid82831d2013-02-20 07:43:19 -06001782{
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001783 return vc_handler(vc);
Anthony Liguorid82831d2013-02-20 07:43:19 -06001784}
1785
1786void register_vc_handler(VcHandler *handler)
1787{
1788 vc_handler = handler;
1789}
1790
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001791void qemu_console_resize(QemuConsole *s, int width, int height)
pbrookc60e08d2008-07-01 16:24:38 +00001792{
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001793 DisplaySurface *surface;
1794
1795 assert(s->console_type == GRAPHIC_CONSOLE);
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001796 surface = qemu_create_displaysurface(width, height);
1797 dpy_gfx_replace_surface(s, surface);
pbrookc60e08d2008-07-01 16:24:38 +00001798}
balrog38334f72008-09-24 02:21:24 +00001799
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001800void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
aliguori3023f332009-01-16 19:04:14 +00001801 int dst_x, int dst_y, int w, int h)
balrogc21bbcf2008-09-24 03:32:33 +00001802{
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001803 assert(con->console_type == GRAPHIC_CONSOLE);
1804 dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
balrog38334f72008-09-24 02:21:24 +00001805}
aliguori7d957bd2009-01-15 22:14:11 +00001806
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001807DisplaySurface *qemu_console_surface(QemuConsole *console)
1808{
Gerd Hoffmann321f0482013-03-12 14:39:22 +01001809 return console->surface;
Gerd Hoffmannc78f7132013-03-05 15:24:14 +01001810}
1811
1812DisplayState *qemu_console_displaystate(QemuConsole *console)
1813{
1814 return console->ds;
1815}
1816
malc0da2ea12009-01-23 19:56:19 +00001817PixelFormat qemu_different_endianness_pixelformat(int bpp)
aliguori7d957bd2009-01-15 22:14:11 +00001818{
1819 PixelFormat pf;
1820
1821 memset(&pf, 0x00, sizeof(PixelFormat));
1822
1823 pf.bits_per_pixel = bpp;
BALATON Zoltanfeadf1a2012-08-22 17:19:42 +02001824 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
aliguori7d957bd2009-01-15 22:14:11 +00001825 pf.depth = bpp == 32 ? 24 : bpp;
1826
1827 switch (bpp) {
malc0da2ea12009-01-23 19:56:19 +00001828 case 24:
1829 pf.rmask = 0x000000FF;
1830 pf.gmask = 0x0000FF00;
1831 pf.bmask = 0x00FF0000;
1832 pf.rmax = 255;
1833 pf.gmax = 255;
1834 pf.bmax = 255;
1835 pf.rshift = 0;
1836 pf.gshift = 8;
1837 pf.bshift = 16;
aliguori90a1e3c2009-01-26 15:37:30 +00001838 pf.rbits = 8;
1839 pf.gbits = 8;
1840 pf.bbits = 8;
aliguori7d957bd2009-01-15 22:14:11 +00001841 break;
malc0da2ea12009-01-23 19:56:19 +00001842 case 32:
1843 pf.rmask = 0x0000FF00;
1844 pf.gmask = 0x00FF0000;
1845 pf.bmask = 0xFF000000;
1846 pf.amask = 0x00000000;
1847 pf.amax = 255;
1848 pf.rmax = 255;
1849 pf.gmax = 255;
1850 pf.bmax = 255;
1851 pf.ashift = 0;
1852 pf.rshift = 8;
1853 pf.gshift = 16;
1854 pf.bshift = 24;
aliguori90a1e3c2009-01-26 15:37:30 +00001855 pf.rbits = 8;
1856 pf.gbits = 8;
1857 pf.bbits = 8;
1858 pf.abits = 8;
malc0da2ea12009-01-23 19:56:19 +00001859 break;
1860 default:
1861 break;
1862 }
1863 return pf;
1864}
1865
1866PixelFormat qemu_default_pixelformat(int bpp)
1867{
1868 PixelFormat pf;
1869
1870 memset(&pf, 0x00, sizeof(PixelFormat));
1871
1872 pf.bits_per_pixel = bpp;
BALATON Zoltanfeadf1a2012-08-22 17:19:42 +02001873 pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
malc0da2ea12009-01-23 19:56:19 +00001874 pf.depth = bpp == 32 ? 24 : bpp;
1875
1876 switch (bpp) {
Gerd Hoffmannb6278082010-05-21 11:59:14 +02001877 case 15:
1878 pf.bits_per_pixel = 16;
Gerd Hoffmannb6278082010-05-21 11:59:14 +02001879 pf.rmask = 0x00007c00;
1880 pf.gmask = 0x000003E0;
1881 pf.bmask = 0x0000001F;
1882 pf.rmax = 31;
1883 pf.gmax = 31;
1884 pf.bmax = 31;
1885 pf.rshift = 10;
1886 pf.gshift = 5;
1887 pf.bshift = 0;
1888 pf.rbits = 5;
1889 pf.gbits = 5;
1890 pf.bbits = 5;
1891 break;
aliguori7d957bd2009-01-15 22:14:11 +00001892 case 16:
1893 pf.rmask = 0x0000F800;
1894 pf.gmask = 0x000007E0;
1895 pf.bmask = 0x0000001F;
1896 pf.rmax = 31;
1897 pf.gmax = 63;
1898 pf.bmax = 31;
1899 pf.rshift = 11;
1900 pf.gshift = 5;
1901 pf.bshift = 0;
aliguori90a1e3c2009-01-26 15:37:30 +00001902 pf.rbits = 5;
1903 pf.gbits = 6;
1904 pf.bbits = 5;
aliguori7d957bd2009-01-15 22:14:11 +00001905 break;
1906 case 24:
aliguori7d957bd2009-01-15 22:14:11 +00001907 pf.rmask = 0x00FF0000;
1908 pf.gmask = 0x0000FF00;
1909 pf.bmask = 0x000000FF;
1910 pf.rmax = 255;
1911 pf.gmax = 255;
1912 pf.bmax = 255;
1913 pf.rshift = 16;
1914 pf.gshift = 8;
1915 pf.bshift = 0;
aliguori90a1e3c2009-01-26 15:37:30 +00001916 pf.rbits = 8;
1917 pf.gbits = 8;
1918 pf.bbits = 8;
Markus Armbruster0eba62e2011-11-22 12:56:10 +01001919 break;
malc0da2ea12009-01-23 19:56:19 +00001920 case 32:
1921 pf.rmask = 0x00FF0000;
1922 pf.gmask = 0x0000FF00;
1923 pf.bmask = 0x000000FF;
malc0da2ea12009-01-23 19:56:19 +00001924 pf.rmax = 255;
1925 pf.gmax = 255;
1926 pf.bmax = 255;
malc0da2ea12009-01-23 19:56:19 +00001927 pf.rshift = 16;
1928 pf.gshift = 8;
1929 pf.bshift = 0;
aliguori90a1e3c2009-01-26 15:37:30 +00001930 pf.rbits = 8;
1931 pf.gbits = 8;
1932 pf.bbits = 8;
aliguori7d957bd2009-01-15 22:14:11 +00001933 break;
1934 default:
1935 break;
1936 }
1937 return pf;
1938}
Anthony Liguori01f45d92013-03-05 23:21:32 +05301939
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001940static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
1941 Error **errp)
1942{
1943 int val;
1944
1945 backend->vc = g_new0(ChardevVC, 1);
1946
1947 val = qemu_opt_get_number(opts, "width", 0);
1948 if (val != 0) {
1949 backend->vc->has_width = true;
1950 backend->vc->width = val;
1951 }
1952
1953 val = qemu_opt_get_number(opts, "height", 0);
1954 if (val != 0) {
1955 backend->vc->has_height = true;
1956 backend->vc->height = val;
1957 }
1958
1959 val = qemu_opt_get_number(opts, "cols", 0);
1960 if (val != 0) {
1961 backend->vc->has_cols = true;
1962 backend->vc->cols = val;
1963 }
1964
1965 val = qemu_opt_get_number(opts, "rows", 0);
1966 if (val != 0) {
1967 backend->vc->has_rows = true;
1968 backend->vc->rows = val;
1969 }
1970}
1971
Gerd Hoffmann95be0662013-04-17 09:45:10 +02001972static const TypeInfo qemu_console_info = {
1973 .name = TYPE_QEMU_CONSOLE,
1974 .parent = TYPE_OBJECT,
1975 .instance_size = sizeof(QemuConsole),
1976 .class_size = sizeof(QemuConsoleClass),
1977};
1978
1979
Anthony Liguori01f45d92013-03-05 23:21:32 +05301980static void register_types(void)
1981{
Gerd Hoffmann95be0662013-04-17 09:45:10 +02001982 type_register_static(&qemu_console_info);
Gerd Hoffmann702ec692013-02-25 15:52:32 +01001983 register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
1984 qemu_chr_parse_vc);
Anthony Liguori01f45d92013-03-05 23:21:32 +05301985}
1986
1987type_init(register_types);