Kevin O'Connor | 27129d0 | 2016-08-04 16:25:33 -0400 | [diff] [blame] | 1 | // Virtual software based cursor support |
| 2 | // |
| 3 | // Copyright (C) 2014-2016 Kevin O'Connor <kevin@koconnor.net> |
| 4 | // |
| 5 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
| 6 | |
| 7 | #include "biosvar.h" // GET_BDA |
Kevin O'Connor | 774f5cd | 2016-08-04 17:02:16 -0400 | [diff] [blame] | 8 | #include "bregs.h" // struct bregs |
Kevin O'Connor | 0397e80 | 2016-08-04 17:53:45 -0400 | [diff] [blame] | 9 | #include "vgabios.h" // get_cursor_pos |
| 10 | #include "vgafb.h" // handle_gfx_op |
Kevin O'Connor | 2f2ec11 | 2016-08-05 11:14:58 -0400 | [diff] [blame] | 11 | #include "vgautil.h" // swcursor_check_event |
Kevin O'Connor | 27129d0 | 2016-08-04 16:25:33 -0400 | [diff] [blame] | 12 | |
| 13 | // Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell |
| 14 | static void |
| 15 | gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp) |
| 16 | { |
| 17 | u16 cursor_type = get_cursor_shape(); |
| 18 | u8 start = cursor_type >> 8, end = cursor_type & 0xff; |
| 19 | struct gfx_op op; |
| 20 | init_gfx_op(&op, vmode_g); |
| 21 | op.x = cp.x * 8; |
| 22 | int cheight = GET_BDA(char_height); |
| 23 | op.y = cp.y * cheight + start; |
| 24 | |
| 25 | int i; |
| 26 | for (i = start; i < cheight && i <= end; i++, op.y++) { |
| 27 | op.op = GO_READ8; |
| 28 | handle_gfx_op(&op); |
| 29 | int j; |
| 30 | for (j = 0; j < 8; j++) |
| 31 | op.pixels[j] ^= 0x07; |
| 32 | op.op = GO_WRITE8; |
| 33 | handle_gfx_op(&op); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | // Draw/undraw a cursor on the screen |
Kevin O'Connor | 774f5cd | 2016-08-04 17:02:16 -0400 | [diff] [blame] | 38 | static void |
| 39 | set_swcursor(int enable) |
Kevin O'Connor | 27129d0 | 2016-08-04 16:25:33 -0400 | [diff] [blame] | 40 | { |
Kevin O'Connor | 27129d0 | 2016-08-04 16:25:33 -0400 | [diff] [blame] | 41 | u8 flags = GET_BDA_EXT(flags); |
| 42 | if (!!(flags & BF_SWCURSOR) == enable) |
| 43 | // Already in requested mode. |
| 44 | return; |
| 45 | struct vgamode_s *vmode_g = get_current_mode(); |
| 46 | if (!vmode_g) |
| 47 | return; |
| 48 | struct cursorpos cp = get_cursor_pos(GET_BDA(video_page)); |
| 49 | if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows) |
| 50 | || GET_BDA(cursor_type) >= 0x2000) |
| 51 | // Cursor not visible |
| 52 | return; |
| 53 | |
| 54 | SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0)); |
| 55 | |
| 56 | if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { |
| 57 | gfx_set_swcursor(vmode_g, enable, cp); |
| 58 | return; |
| 59 | } |
| 60 | |
| 61 | // In text mode, swap foreground and background attributes for cursor |
| 62 | void *dest_far = text_address(cp) + 1; |
| 63 | u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far); |
| 64 | attr = (attr >> 4) | (attr << 4); |
| 65 | SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr); |
| 66 | } |
Kevin O'Connor | 774f5cd | 2016-08-04 17:02:16 -0400 | [diff] [blame] | 67 | |
| 68 | // Disable virtual cursor if a vgabios call accesses the framebuffer |
| 69 | void |
| 70 | swcursor_pre_handle10(struct bregs *regs) |
| 71 | { |
| 72 | if (!vga_emulate_text()) |
| 73 | return; |
| 74 | switch (regs->ah) { |
| 75 | case 0x4f: |
| 76 | if (!CONFIG_VGA_VBE || regs->al != 0x02) |
| 77 | break; |
| 78 | // NO BREAK |
| 79 | case 0x00 ... 0x02: |
| 80 | case 0x05 ... 0x0e: |
| 81 | case 0x13: |
| 82 | set_swcursor(0); |
| 83 | break; |
| 84 | default: |
| 85 | break; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | // Called by periodic (18.2hz) timer |
| 90 | void |
| 91 | swcursor_check_event(void) |
| 92 | { |
| 93 | if (!vga_emulate_text()) |
| 94 | return; |
| 95 | set_swcursor(GET_BDA(timer_counter) % 18 < 9); |
| 96 | } |