6328832fba
item entry. * The bios_ia32 video platform code now stores the available VESA modes in the new vesa_modes kernel_args field. * When configuring a VESA mode via settings file, it's no longer needed to specify the exact mode - the closest available mode is now used. This should help with bug #1962. * frame_buffer_console_init() now also creates a boot_item for the VESA modes in the kernel_args. * The VESA accelerant now filters the mode list to only contain modes that are actually supported. * Moved non-shared vesa driver data into its own file vesa_private.h. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24675 a95241bf-73f2-0310-859d-f6bbb57e9c96
463 lines
9.9 KiB
C++
463 lines
9.9 KiB
C++
/*
|
|
* Copyright 2005-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include <KernelExport.h>
|
|
#include <kernel.h>
|
|
#include <lock.h>
|
|
#include <boot_item.h>
|
|
#include <vm.h>
|
|
#include <fs/devfs.h>
|
|
#include <boot/kernel_args.h>
|
|
#include <vesa_info.h>
|
|
|
|
#include <frame_buffer_console.h>
|
|
#include "font.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
|
|
//#define TRACE_FB_CONSOLE
|
|
#ifdef TRACE_FB_CONSOLE
|
|
# define TRACE(x) dprintf x
|
|
#else
|
|
# define TRACE(x) ;
|
|
#endif
|
|
|
|
|
|
struct console_info {
|
|
mutex lock;
|
|
area_id area;
|
|
|
|
addr_t frame_buffer;
|
|
int32 width;
|
|
int32 height;
|
|
int32 depth;
|
|
int32 bytes_per_pixel;
|
|
int32 bytes_per_row;
|
|
|
|
int32 columns;
|
|
int32 rows;
|
|
int32 cursor_x;
|
|
int32 cursor_y;
|
|
};
|
|
|
|
// Palette is (white and black are exchanged):
|
|
// 0 - white, 1 - blue, 2 - green, 3 - cyan, 4 - red, 5 - magenta, 6 - yellow, 7 - black
|
|
// 8-15 - same but bright (we're ignoring those)
|
|
|
|
static uint8 sPalette8[] = {
|
|
63, 32, 52, 70, 42, 88, 67, 0,
|
|
};
|
|
static uint16 sPalette15[] = {
|
|
// 0bbbbbgggggrrrrr (5-5-5)
|
|
0x7fff, 0x001f, 0x03e0, 0x03ff, 0x7c00, 0x7c1f, 0x7fe0, 0x0000,
|
|
};
|
|
static uint16 sPalette16[] = {
|
|
// bbbbbggggggrrrrr (5-6-5)
|
|
0xffff, 0x001f, 0x07e0, 0x07ff, 0xf800, 0xf81f, 0xffe0, 0x0000,
|
|
};
|
|
static uint32 sPalette32[] = {
|
|
// is also used by 24 bit modes
|
|
0xffffff, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00, 0x000000,
|
|
};
|
|
|
|
static struct console_info sConsole;
|
|
static struct frame_buffer_boot_info sBootInfo;
|
|
static struct vesa_mode *sVesaModes;
|
|
|
|
|
|
static inline uint8
|
|
foreground_color(uint8 attr)
|
|
{
|
|
return attr & 0x7;
|
|
}
|
|
|
|
|
|
static inline uint8
|
|
background_color(uint8 attr)
|
|
{
|
|
return (attr >> 4) & 0x7;
|
|
}
|
|
|
|
|
|
static uint8 *
|
|
get_palette_entry(uint8 index)
|
|
{
|
|
switch (sConsole.depth) {
|
|
case 8:
|
|
return &sPalette8[index];
|
|
case 15:
|
|
return (uint8 *)&sPalette15[index];
|
|
case 16:
|
|
return (uint8 *)&sPalette16[index];
|
|
default:
|
|
return (uint8 *)&sPalette32[index];
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
render_glyph(int32 x, int32 y, uint8 glyph, uint8 attr)
|
|
{
|
|
// we're ASCII only
|
|
if (glyph > 127)
|
|
glyph = 127;
|
|
|
|
if (sConsole.depth >= 8) {
|
|
uint8 *base = (uint8 *)(sConsole.frame_buffer + sConsole.bytes_per_row * y * CHAR_HEIGHT
|
|
+ x * CHAR_WIDTH * sConsole.bytes_per_pixel);
|
|
uint8 *color = get_palette_entry(foreground_color(attr));
|
|
uint8 *backgroundColor = get_palette_entry(background_color(attr));
|
|
|
|
for (y = 0; y < CHAR_HEIGHT; y++) {
|
|
uint8 bits = FONT[CHAR_HEIGHT * glyph + y];
|
|
for (x = 0; x < CHAR_WIDTH; x++) {
|
|
for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) {
|
|
if (bits & 1)
|
|
base[x * sConsole.bytes_per_pixel + i] = color[i];
|
|
else
|
|
base[x * sConsole.bytes_per_pixel + i] = backgroundColor[i];
|
|
}
|
|
bits >>= 1;
|
|
}
|
|
|
|
base += sConsole.bytes_per_row;
|
|
}
|
|
} else {
|
|
// monochrome mode
|
|
|
|
uint8 *base = (uint8 *)(sConsole.frame_buffer + sConsole.bytes_per_row * y * CHAR_HEIGHT
|
|
+ x * CHAR_WIDTH / 8);
|
|
uint8 baseOffset = (x * CHAR_WIDTH) & 0x7;
|
|
|
|
for (y = 0; y < CHAR_HEIGHT; y++) {
|
|
uint8 bits = FONT[CHAR_HEIGHT * glyph + y];
|
|
uint8 offset = baseOffset;
|
|
uint8 mask = 1 << (7 - baseOffset);
|
|
|
|
for (x = 0; x < CHAR_WIDTH; x++) {
|
|
if (mask == 0)
|
|
mask = 128;
|
|
|
|
// black on white
|
|
if (bits & 1)
|
|
base[offset / 8] &= ~mask;
|
|
else
|
|
base[offset / 8] |= mask;
|
|
|
|
bits >>= 1;
|
|
mask >>= 1;
|
|
offset += 1;
|
|
}
|
|
|
|
base += sConsole.bytes_per_row;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
draw_cursor(int32 x, int32 y)
|
|
{
|
|
if (x < 0 || y < 0)
|
|
return;
|
|
|
|
x *= CHAR_WIDTH * sConsole.bytes_per_pixel;
|
|
y *= CHAR_HEIGHT;
|
|
int32 endX = x + CHAR_WIDTH * sConsole.bytes_per_pixel;
|
|
int32 endY = y + CHAR_HEIGHT;
|
|
uint8 *base = (uint8 *)(sConsole.frame_buffer + y * sConsole.bytes_per_row);
|
|
|
|
if (sConsole.depth < 8) {
|
|
x /= 8;
|
|
endY /= 8;
|
|
}
|
|
|
|
for (; y < endY; y++) {
|
|
for (int32 x2 = x; x2 < endX; x2++)
|
|
base[x2] = ~base[x2];
|
|
|
|
base += sConsole.bytes_per_row;
|
|
}
|
|
}
|
|
|
|
|
|
static status_t
|
|
console_get_size(int32 *_width, int32 *_height)
|
|
{
|
|
*_width = sConsole.columns;
|
|
*_height = sConsole.rows;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
console_move_cursor(int32 x, int32 y)
|
|
{
|
|
if (!frame_buffer_console_available())
|
|
return;
|
|
|
|
draw_cursor(sConsole.cursor_x, sConsole.cursor_y);
|
|
draw_cursor(x, y);
|
|
|
|
sConsole.cursor_x = x;
|
|
sConsole.cursor_y = y;
|
|
}
|
|
|
|
|
|
static void
|
|
console_put_glyph(int32 x, int32 y, uint8 glyph, uint8 attr)
|
|
{
|
|
if (x >= sConsole.columns || y >= sConsole.rows
|
|
|| !frame_buffer_console_available())
|
|
return;
|
|
|
|
render_glyph(x, y, glyph, attr);
|
|
}
|
|
|
|
|
|
static void
|
|
console_fill_glyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph,
|
|
uint8 attr)
|
|
{
|
|
if (x >= sConsole.columns || y >= sConsole.rows
|
|
|| !frame_buffer_console_available())
|
|
return;
|
|
|
|
int32 left = x + width;
|
|
if (left > sConsole.columns)
|
|
left = sConsole.columns;
|
|
|
|
int32 bottom = y + height;
|
|
if (bottom > sConsole.rows)
|
|
bottom = sConsole.rows;
|
|
|
|
for (; y < bottom; y++) {
|
|
for (int32 x2 = x; x2 < left; x2++) {
|
|
render_glyph(x2, y, glyph, attr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
console_blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx,
|
|
int32 desty)
|
|
{
|
|
if (!frame_buffer_console_available())
|
|
return;
|
|
|
|
height *= CHAR_HEIGHT;
|
|
srcy *= CHAR_HEIGHT;
|
|
desty *= CHAR_HEIGHT;
|
|
|
|
if (sConsole.depth >= 8) {
|
|
width *= CHAR_WIDTH * sConsole.bytes_per_pixel;
|
|
srcx *= CHAR_WIDTH * sConsole.bytes_per_pixel;
|
|
destx *= CHAR_WIDTH * sConsole.bytes_per_pixel;
|
|
} else {
|
|
// monochrome mode
|
|
width = width * CHAR_WIDTH / 8;
|
|
srcx = srcx * CHAR_WIDTH / 8;
|
|
destx = destx * CHAR_WIDTH / 8;
|
|
}
|
|
|
|
for (int32 y = 0; y < height; y++) {
|
|
memmove((void *)(sConsole.frame_buffer + (desty + y)
|
|
* sConsole.bytes_per_row + destx),
|
|
(void *)(sConsole.frame_buffer + (srcy + y) * sConsole.bytes_per_row
|
|
+ srcx), width);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
console_clear(uint8 attr)
|
|
{
|
|
if (!frame_buffer_console_available())
|
|
return;
|
|
|
|
switch (sConsole.bytes_per_pixel) {
|
|
case 1:
|
|
if (sConsole.depth >= 8) {
|
|
memset((void *)sConsole.frame_buffer,
|
|
sPalette8[background_color(attr)],
|
|
sConsole.height * sConsole.bytes_per_row);
|
|
} else {
|
|
// special case for VGA mode
|
|
memset((void *)sConsole.frame_buffer, 0xff,
|
|
sConsole.height * sConsole.bytes_per_row);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
uint8 *base = (uint8 *)sConsole.frame_buffer;
|
|
uint8 *color = get_palette_entry(background_color(attr));
|
|
|
|
for (int32 y = 0; y < sConsole.height; y++) {
|
|
for (int32 x = 0; x < sConsole.width; x++) {
|
|
for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) {
|
|
base[x * sConsole.bytes_per_pixel + i] = color[i];
|
|
}
|
|
}
|
|
base += sConsole.bytes_per_row;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
sConsole.cursor_x = -1;
|
|
sConsole.cursor_y = -1;
|
|
}
|
|
|
|
|
|
static status_t
|
|
console_std_ops(int32 op, ...)
|
|
{
|
|
switch (op) {
|
|
case B_MODULE_INIT:
|
|
return frame_buffer_console_available() ? B_OK : B_ERROR;
|
|
case B_MODULE_UNINIT:
|
|
return B_OK;
|
|
|
|
default:
|
|
return B_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
console_module_info gFrameBufferConsoleModule = {
|
|
{
|
|
FRAME_BUFFER_CONSOLE_MODULE_NAME,
|
|
0,
|
|
console_std_ops
|
|
},
|
|
&console_get_size,
|
|
&console_move_cursor,
|
|
&console_put_glyph,
|
|
&console_fill_glyph,
|
|
&console_blit,
|
|
&console_clear,
|
|
};
|
|
|
|
|
|
static status_t
|
|
frame_buffer_update(addr_t baseAddress, int32 width, int32 height, int32 depth, int32 bytesPerRow)
|
|
{
|
|
TRACE(("frame_buffer_update(buffer = %p, width = %ld, height = %ld, depth = %ld, bytesPerRow = %ld)\n",
|
|
(void *)baseAddress, width, height, depth, bytesPerRow));
|
|
|
|
mutex_lock(&sConsole.lock);
|
|
|
|
sConsole.frame_buffer = baseAddress;
|
|
sConsole.width = width;
|
|
sConsole.height = height;
|
|
sConsole.depth = depth;
|
|
sConsole.bytes_per_pixel = (depth + 7) / 8;
|
|
sConsole.bytes_per_row = bytesPerRow;
|
|
sConsole.columns = sConsole.width / CHAR_WIDTH;
|
|
sConsole.rows = sConsole.height / CHAR_HEIGHT;
|
|
|
|
// initially, the cursor is hidden
|
|
sConsole.cursor_x = -1;
|
|
sConsole.cursor_y = -1;
|
|
|
|
TRACE(("framebuffer mapped at %p, %ld columns, %ld rows\n",
|
|
(void *)sConsole.frame_buffer, sConsole.columns, sConsole.rows));
|
|
|
|
mutex_unlock(&sConsole.lock);
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
bool
|
|
frame_buffer_console_available(void)
|
|
{
|
|
return sConsole.frame_buffer != 0;
|
|
}
|
|
|
|
|
|
status_t
|
|
frame_buffer_console_init(kernel_args *args)
|
|
{
|
|
if (!args->frame_buffer.enabled)
|
|
return B_OK;
|
|
|
|
void *frameBuffer;
|
|
sConsole.area = map_physical_memory("vesa_fb",
|
|
(void *)args->frame_buffer.physical_buffer.start,
|
|
args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS,
|
|
B_READ_AREA | B_WRITE_AREA | B_USER_CLONEABLE_AREA, &frameBuffer);
|
|
if (sConsole.area < B_OK)
|
|
return sConsole.area;
|
|
|
|
if (args->frame_buffer.depth == 4) {
|
|
// VGA mode will be treated as monochrome
|
|
args->frame_buffer.bytes_per_row /= 8;
|
|
}
|
|
|
|
frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width,
|
|
args->frame_buffer.height, args->frame_buffer.depth,
|
|
args->frame_buffer.bytes_per_row);
|
|
|
|
sBootInfo.frame_buffer = (addr_t)frameBuffer;
|
|
sBootInfo.width = args->frame_buffer.width;
|
|
sBootInfo.height = args->frame_buffer.height;
|
|
sBootInfo.depth = args->frame_buffer.depth;
|
|
sBootInfo.bytes_per_row = args->frame_buffer.bytes_per_row;
|
|
add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo,
|
|
sizeof(frame_buffer_boot_info));
|
|
|
|
sVesaModes = (vesa_mode *)malloc(args->vesa_modes_size);
|
|
if (sVesaModes != NULL) {
|
|
memcpy(sVesaModes, args->vesa_modes, args->vesa_modes_size);
|
|
add_boot_item(VESA_MODES_BOOT_INFO, sVesaModes, args->vesa_modes_size);
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
frame_buffer_console_init_post_modules(kernel_args *args)
|
|
{
|
|
mutex_init(&sConsole.lock, "console_lock");
|
|
|
|
if (sConsole.frame_buffer == 0)
|
|
return B_OK;
|
|
|
|
// try to set frame buffer memory to write combined
|
|
|
|
return vm_set_area_memory_type(sConsole.area,
|
|
args->frame_buffer.physical_buffer.start, B_MTR_WC);
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
status_t
|
|
_user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height,
|
|
int32 depth, int32 bytesPerRow)
|
|
{
|
|
debug_stop_screen_debug_output();
|
|
|
|
if (geteuid() != 0)
|
|
return B_NOT_ALLOWED;
|
|
if (IS_USER_ADDRESS(baseAddress) && baseAddress != 0)
|
|
return B_BAD_ADDRESS;
|
|
|
|
return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow);
|
|
}
|
|
|