Refactor graphical resolution changes.
This commit is contained in:
parent
0342e03073
commit
e7c5d032d1
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2014 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2014, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -38,7 +38,10 @@
|
||||||
#include <sortix/kernel/textbuffer.h>
|
#include <sortix/kernel/textbuffer.h>
|
||||||
#include <sortix/kernel/video.h>
|
#include <sortix/kernel/video.h>
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
#include "x86-family/memorymanagement.h"
|
#include "x86-family/memorymanagement.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "lfbtextbuffer.h"
|
#include "lfbtextbuffer.h"
|
||||||
#include "bga.h"
|
#include "bga.h"
|
||||||
|
|
||||||
|
@ -100,20 +103,22 @@ public:
|
||||||
virtual ~BGADevice();
|
virtual ~BGADevice();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual struct dispmsg_crtc_mode GetCurrentMode(uint64_t connector) const;
|
virtual uint64_t GetConnectorCount();
|
||||||
|
virtual bool GetDefaultMode(uint64_t connector, struct dispmsg_crtc_mode* mode);
|
||||||
|
virtual bool GetCurrentMode(uint64_t connector, struct dispmsg_crtc_mode* mode);
|
||||||
virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode);
|
virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode);
|
||||||
virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const;
|
virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode);
|
||||||
virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* num_modes) const;
|
virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* num_modes);
|
||||||
virtual off_t FrameSize() const;
|
virtual off_t FrameSize();
|
||||||
virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count);
|
virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count);
|
||||||
virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count);
|
virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count);
|
||||||
virtual TextBuffer* CreateTextBuffer(uint64_t connector);
|
virtual TextBuffer* CreateTextBuffer(uint64_t connector, struct dispmsg_crtc_mode mode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool DetectModes() const;
|
bool DetectModes();
|
||||||
uint16_t WriteRegister(uint16_t index, uint16_t value);
|
uint16_t WriteRegister(uint16_t index, uint16_t value);
|
||||||
uint16_t ReadRegister(uint16_t index);
|
uint16_t ReadRegister(uint16_t index);
|
||||||
uint16_t GetCapability(uint16_t index);
|
uint16_t GetCapability(uint16_t index);
|
||||||
|
@ -121,8 +126,8 @@ private:
|
||||||
bool SupportsResolution(uint16_t width, uint16_t height, uint16_t depth);
|
bool SupportsResolution(uint16_t width, uint16_t height, uint16_t depth);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable size_t num_modes;
|
size_t num_modes;
|
||||||
mutable struct dispmsg_crtc_mode* modes;
|
struct dispmsg_crtc_mode* modes;
|
||||||
struct dispmsg_crtc_mode current_mode;
|
struct dispmsg_crtc_mode current_mode;
|
||||||
addralloc_t fb_alloc;
|
addralloc_t fb_alloc;
|
||||||
addralloc_t mmio_alloc;
|
addralloc_t mmio_alloc;
|
||||||
|
@ -223,32 +228,91 @@ bool BGADevice::SetVideoMode(uint16_t width, uint16_t height, uint16_t depth, bo
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t BGADevice::GetConnectorCount()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Need a better method of detecting available/desired resolutions.
|
// TODO: Need a better method of detecting available/desired resolutions.
|
||||||
bool BGADevice::SupportsResolution(uint16_t width, uint16_t height, uint16_t depth)
|
bool BGADevice::SupportsResolution(uint16_t width, uint16_t height, uint16_t depth)
|
||||||
{
|
{
|
||||||
if ( !width || !height || !depth )
|
if ( !width || !height || !depth )
|
||||||
return false;
|
return errno = EINVAL, false;
|
||||||
if ( maxxres < width || maxyres < height || maxbpp < depth )
|
if ( maxxres < width || maxyres < height || maxbpp < depth )
|
||||||
return false;
|
return errno = EINVAL, false;
|
||||||
// TODO: Is this actually a restriction?
|
// TODO: Is this actually a restriction?
|
||||||
if ( width % 8U )
|
// TODO: This is not a restriction in VirtualBox anymore at least.
|
||||||
return false;
|
//if ( width % 8U )
|
||||||
|
// return errno = EINVAL, false;
|
||||||
// TODO: Can we determine this more closely in advance? Perhaps if the
|
// TODO: Can we determine this more closely in advance? Perhaps if the
|
||||||
// framebuffer we will be using is larger than video memory?
|
// framebuffer we will be using is larger than video memory?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dispmsg_crtc_mode BGADevice::GetCurrentMode(uint64_t connector) const
|
bool BGADevice::GetDefaultMode(uint64_t connector,
|
||||||
|
struct dispmsg_crtc_mode* mode_out)
|
||||||
{
|
{
|
||||||
if ( connector != 0 )
|
if ( connector )
|
||||||
|
return errno = EINVAL, false;
|
||||||
|
|
||||||
|
bool good = false;
|
||||||
|
uint32_t xres;
|
||||||
|
uint32_t yres;
|
||||||
|
uint32_t bpp;
|
||||||
|
if ( connector == 0 && Log::fallback_framebuffer &&
|
||||||
|
SupportsResolution(Log::fallback_framebuffer_width,
|
||||||
|
Log::fallback_framebuffer_height,
|
||||||
|
32) )
|
||||||
{
|
{
|
||||||
errno = EINVAL;
|
xres = Log::fallback_framebuffer_width;
|
||||||
struct dispmsg_crtc_mode mode;
|
yres = Log::fallback_framebuffer_height;
|
||||||
memset(&mode, 0, sizeof(mode));
|
bpp = 32;
|
||||||
return mode;
|
}
|
||||||
|
else if ( connector == 0 && Log::fallback_framebuffer &&
|
||||||
|
SupportsResolution(Log::fallback_framebuffer_width,
|
||||||
|
Log::fallback_framebuffer_height,
|
||||||
|
Log::fallback_framebuffer_bpp) )
|
||||||
|
{
|
||||||
|
xres = Log::fallback_framebuffer_width;
|
||||||
|
yres = Log::fallback_framebuffer_height;
|
||||||
|
bpp = Log::fallback_framebuffer_bpp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return errno = EINVAL, false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_mode;
|
struct dispmsg_crtc_mode mode;
|
||||||
|
memset(&mode, 0, sizeof(0));
|
||||||
|
mode.driver_index = 0;
|
||||||
|
mode.magic = 0;
|
||||||
|
mode.control = DISPMSG_CONTROL_VALID | DISPMSG_CONTROL_DEFAULT;
|
||||||
|
if ( good )
|
||||||
|
mode.control |= DISPMSG_CONTROL_GOOD_DEFAULT;
|
||||||
|
mode.fb_format = bpp;
|
||||||
|
mode.view_xres = xres;
|
||||||
|
mode.view_yres = yres;
|
||||||
|
mode.fb_location = 0;
|
||||||
|
mode.pitch = xres * (bpp + 7) / 8;
|
||||||
|
mode.surf_off_x = 0;
|
||||||
|
mode.surf_off_y = 0;
|
||||||
|
mode.start_x = 0;
|
||||||
|
mode.start_y = 0;
|
||||||
|
mode.end_x = 0;
|
||||||
|
mode.end_y = 0;
|
||||||
|
mode.desktop_height = yres;
|
||||||
|
*mode_out = mode;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BGADevice::GetCurrentMode(uint64_t connector,
|
||||||
|
struct dispmsg_crtc_mode* mode)
|
||||||
|
{
|
||||||
|
if ( connector != 0 )
|
||||||
|
return false;
|
||||||
|
*mode = current_mode;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BGADevice::SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode)
|
bool BGADevice::SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode)
|
||||||
|
@ -259,13 +323,6 @@ bool BGADevice::SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode)
|
||||||
if ( connector != 0 )
|
if ( connector != 0 )
|
||||||
return errno = EINVAL, false;
|
return errno = EINVAL, false;
|
||||||
|
|
||||||
size_t new_framesize = (size_t) mode.view_xres *
|
|
||||||
(size_t) mode.view_yres *
|
|
||||||
((size_t) mode.fb_format + 7) / 8UL;
|
|
||||||
// TODO: Use a better error code than ENOSPC?
|
|
||||||
if ( fb_alloc.size < new_framesize )
|
|
||||||
return errno = ENOSPC, false;
|
|
||||||
|
|
||||||
if ( !SetVideoMode(mode.view_xres, mode.view_yres, mode.fb_format, false) )
|
if ( !SetVideoMode(mode.view_xres, mode.view_yres, mode.fb_format, false) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -274,7 +331,7 @@ bool BGADevice::SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BGADevice::Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const
|
bool BGADevice::Supports(uint64_t connector, struct dispmsg_crtc_mode mode)
|
||||||
{
|
{
|
||||||
if ( connector != 0 )
|
if ( connector != 0 )
|
||||||
return errno = EINVAL, false;
|
return errno = EINVAL, false;
|
||||||
|
@ -303,10 +360,17 @@ bool BGADevice::Supports(uint64_t connector, struct dispmsg_crtc_mode mode) cons
|
||||||
mode.fb_format != VBE_DISPI_BPP_32 )
|
mode.fb_format != VBE_DISPI_BPP_32 )
|
||||||
return errno = ENOSYS, false;
|
return errno = ENOSYS, false;
|
||||||
|
|
||||||
return ((BGADevice*) this)->SupportsResolution(mode.view_xres, mode.view_yres, mode.fb_format);
|
size_t new_framesize = (size_t) mode.view_xres *
|
||||||
|
(size_t) mode.view_yres *
|
||||||
|
((size_t) mode.fb_format + 7) / 8UL;
|
||||||
|
// TODO: Use a better error code than ENOSPC?
|
||||||
|
if ( fb_alloc.size < new_framesize )
|
||||||
|
return errno = ENOSPC, false;
|
||||||
|
|
||||||
|
return SupportsResolution(mode.view_xres, mode.view_yres, mode.fb_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dispmsg_crtc_mode* BGADevice::GetModes(uint64_t connector, size_t* retnum) const
|
struct dispmsg_crtc_mode* BGADevice::GetModes(uint64_t connector, size_t* retnum)
|
||||||
{
|
{
|
||||||
if ( connector != 0 )
|
if ( connector != 0 )
|
||||||
return errno = EINVAL, (struct dispmsg_crtc_mode*) NULL;
|
return errno = EINVAL, (struct dispmsg_crtc_mode*) NULL;
|
||||||
|
@ -322,7 +386,7 @@ struct dispmsg_crtc_mode* BGADevice::GetModes(uint64_t connector, size_t* retnum
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t BGADevice::FrameSize() const
|
off_t BGADevice::FrameSize()
|
||||||
{
|
{
|
||||||
return (off_t) fb_alloc.size;
|
return (off_t) fb_alloc.size;
|
||||||
}
|
}
|
||||||
|
@ -351,7 +415,7 @@ ssize_t BGADevice::ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BGADevice::DetectModes() const
|
bool BGADevice::DetectModes()
|
||||||
{
|
{
|
||||||
num_modes = 0;
|
num_modes = 0;
|
||||||
unsigned bpp = VBE_DISPI_BPP_32;
|
unsigned bpp = VBE_DISPI_BPP_32;
|
||||||
|
@ -401,15 +465,19 @@ bool BGADevice::DetectModes() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBuffer* BGADevice::CreateTextBuffer(uint64_t connector)
|
TextBuffer* BGADevice::CreateTextBuffer(uint64_t connector,
|
||||||
|
struct dispmsg_crtc_mode mode)
|
||||||
{
|
{
|
||||||
|
if ( !Supports(connector, mode) )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if ( connector != 0 )
|
if ( connector != 0 )
|
||||||
return errno = EINVAL, (TextBuffer*) NULL;
|
return errno = EINVAL, (TextBuffer*) NULL;
|
||||||
|
|
||||||
uint8_t* lfb = (uint8_t*) fb_alloc.from;
|
uint8_t* lfb = (uint8_t*) fb_alloc.from;
|
||||||
uint32_t lfbformat = current_mode.fb_format;
|
uint32_t lfbformat = mode.fb_format;
|
||||||
size_t scansize = current_mode.view_xres * current_mode.fb_format / 8UL;
|
size_t scansize = mode.view_xres * mode.fb_format / 8UL;
|
||||||
return CreateLFBTextBuffer(lfb, lfbformat, current_mode.view_xres, current_mode.view_yres, scansize);
|
return CreateLFBTextBuffer(lfb, lfbformat, mode.view_xres, mode.view_yres, scansize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TryInitializeDevice(uint32_t devaddr)
|
static void TryInitializeDevice(uint32_t devaddr)
|
||||||
|
@ -498,6 +566,8 @@ static void TryInitializeDevice(uint32_t devaddr)
|
||||||
delete bga_device;
|
delete bga_device;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Video::ConfigureDevice(bga_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
|
|
|
@ -33,6 +33,9 @@ static const uint32_t DISPMSG_CONTROL_VALID = 1 << 0;
|
||||||
static const uint32_t DISPMSG_CONTROL_VGA = 1 << 1;
|
static const uint32_t DISPMSG_CONTROL_VGA = 1 << 1;
|
||||||
static const uint32_t DISPMSG_CONTROL_OTHER_RESOLUTIONS = 1 << 2;
|
static const uint32_t DISPMSG_CONTROL_OTHER_RESOLUTIONS = 1 << 2;
|
||||||
static const uint32_t DISPMSG_CONTROL_FALLBACK = 1 << 3;
|
static const uint32_t DISPMSG_CONTROL_FALLBACK = 1 << 3;
|
||||||
|
static const uint32_t DISPMSG_CONTROL_DEFAULT = 1 << 4;
|
||||||
|
static const uint32_t DISPMSG_CONTROL_GOOD_DEFAULT = 1 << 5;
|
||||||
|
static const uint32_t DISPMSG_CONTROL_VM_AUTO_SCALE = 1 << 6;
|
||||||
|
|
||||||
struct dispmsg_string
|
struct dispmsg_string
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -30,6 +30,7 @@ typedef struct multiboot_info multiboot_info_t;
|
||||||
|
|
||||||
namespace Sortix {
|
namespace Sortix {
|
||||||
|
|
||||||
|
class TextBuffer;
|
||||||
class TextBufferHandle;
|
class TextBufferHandle;
|
||||||
|
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
@ -126,6 +127,9 @@ inline size_t PrintFV(const char* format, va_list list)
|
||||||
|
|
||||||
void Init(multiboot_info_t* bootinfo);
|
void Init(multiboot_info_t* bootinfo);
|
||||||
void Center(const char* string);
|
void Center(const char* string);
|
||||||
|
void BeginReplace();
|
||||||
|
void CancelReplace();
|
||||||
|
void FinishReplace(TextBuffer* textbuf);
|
||||||
|
|
||||||
} // namespace Log
|
} // namespace Log
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -76,22 +76,24 @@ class TextBuffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~TextBuffer() { }
|
virtual ~TextBuffer() { }
|
||||||
virtual size_t Width() const = 0;
|
virtual size_t Width() = 0;
|
||||||
virtual size_t Height() const = 0;
|
virtual size_t Height() = 0;
|
||||||
virtual TextChar GetChar(TextPos pos) const = 0;
|
virtual TextChar GetChar(TextPos pos) = 0;
|
||||||
virtual void SetChar(TextPos pos, TextChar c) = 0;
|
virtual void SetChar(TextPos pos, TextChar c) = 0;
|
||||||
virtual void Scroll(ssize_t off, TextChar fillwith) = 0;
|
virtual void Scroll(ssize_t off, TextChar fillwith) = 0;
|
||||||
virtual void Move(TextPos to, TextPos from, size_t numchars) = 0;
|
virtual void Move(TextPos to, TextPos from, size_t numchars) = 0;
|
||||||
virtual void Fill(TextPos from, TextPos to, TextChar fillwith) = 0;
|
virtual void Fill(TextPos from, TextPos to, TextChar fillwith) = 0;
|
||||||
virtual bool GetCursorEnabled() const = 0;
|
virtual bool GetCursorEnabled() = 0;
|
||||||
virtual void SetCursorEnabled(bool enablecursor) = 0;
|
virtual void SetCursorEnabled(bool enablecursor) = 0;
|
||||||
virtual TextPos GetCursorPos() const = 0;
|
virtual TextPos GetCursorPos() = 0;
|
||||||
virtual void SetCursorPos(TextPos cursorpos) = 0;
|
virtual void SetCursorPos(TextPos cursorpos) = 0;
|
||||||
virtual void SpawnThreads() = 0;
|
virtual void SpawnThreads() = 0;
|
||||||
virtual void Invalidate() = 0;
|
virtual void Invalidate() = 0;
|
||||||
virtual bool EmergencyIsImpaired() = 0;
|
virtual bool EmergencyIsImpaired() = 0;
|
||||||
virtual bool EmergencyRecoup() = 0;
|
virtual bool EmergencyRecoup() = 0;
|
||||||
virtual void EmergencyReset() = 0;
|
virtual void EmergencyReset() = 0;
|
||||||
|
virtual void Resume() = 0;
|
||||||
|
virtual void Pause() = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,7 +109,9 @@ public:
|
||||||
~TextBufferHandle();
|
~TextBufferHandle();
|
||||||
TextBuffer* Acquire();
|
TextBuffer* Acquire();
|
||||||
void Release(TextBuffer* textbuf);
|
void Release(TextBuffer* textbuf);
|
||||||
void Replace(TextBuffer* newtextbuf);
|
void BeginReplace();
|
||||||
|
void CancelReplace();
|
||||||
|
void FinishReplace(TextBuffer* newtextbuf);
|
||||||
bool EmergencyIsImpaired();
|
bool EmergencyIsImpaired();
|
||||||
bool EmergencyRecoup();
|
bool EmergencyRecoup();
|
||||||
void EmergencyReset();
|
void EmergencyReset();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -36,14 +36,19 @@ class VideoDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~VideoDevice() { }
|
virtual ~VideoDevice() { }
|
||||||
virtual struct dispmsg_crtc_mode GetCurrentMode(uint64_t connector) const = 0;
|
virtual uint64_t GetConnectorCount() = 0;
|
||||||
|
virtual bool GetDefaultMode(uint64_t connector, struct dispmsg_crtc_mode* mode) = 0;
|
||||||
|
virtual bool GetCurrentMode(uint64_t connector, struct dispmsg_crtc_mode* mode) = 0;
|
||||||
virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode) = 0;
|
virtual bool SwitchMode(uint64_t connector, struct dispmsg_crtc_mode mode) = 0;
|
||||||
virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) const = 0;
|
virtual bool Supports(uint64_t connector, struct dispmsg_crtc_mode mode) = 0;
|
||||||
virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* nummodes) const = 0;
|
virtual struct dispmsg_crtc_mode* GetModes(uint64_t connector, size_t* nummodes) = 0;
|
||||||
virtual off_t FrameSize() const = 0;
|
virtual off_t FrameSize() = 0;
|
||||||
virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count) = 0;
|
virtual ssize_t WriteAt(ioctx_t* ctx, off_t off, const void* buf, size_t count) = 0;
|
||||||
virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count) = 0;
|
virtual ssize_t ReadAt(ioctx_t* ctx, off_t off, void* buf, size_t count) = 0;
|
||||||
virtual TextBuffer* CreateTextBuffer(uint64_t connector) = 0;
|
virtual TextBuffer* CreateTextBuffer(uint64_t connector, struct dispmsg_crtc_mode mode) = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint64_t device_index;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,6 +58,9 @@ namespace Sortix {
|
||||||
namespace Video {
|
namespace Video {
|
||||||
|
|
||||||
bool RegisterDevice(const char* name, VideoDevice* device);
|
bool RegisterDevice(const char* name, VideoDevice* device);
|
||||||
|
bool ConfigureDevice(VideoDevice* device);
|
||||||
|
bool ResizeDisplay(uint64_t device, uint64_t connector, uint32_t xres,
|
||||||
|
uint32_t yres, uint32_t bpp);
|
||||||
|
|
||||||
} // namespace Video
|
} // namespace Video
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -105,10 +105,10 @@ LFBTextBuffer* CreateLFBTextBuffer(uint8_t* lfb, uint32_t lfbformat,
|
||||||
}
|
}
|
||||||
ret->cursorenabled = true;
|
ret->cursorenabled = true;
|
||||||
ret->cursorpos = TextPos(0, 0);
|
ret->cursorpos = TextPos(0, 0);
|
||||||
for ( size_t y = 0; y < yres; y++ )
|
|
||||||
memset(lfb + scansize * y, 0, ret->bytes_per_pixel * xres);
|
|
||||||
ret->emergency_state = false;
|
ret->emergency_state = false;
|
||||||
ret->invalidated = false;
|
ret->invalidated = false;
|
||||||
|
ret->need_clear = true;
|
||||||
|
ret->exit_after_pause = false;
|
||||||
|
|
||||||
if ( !kernel_process )
|
if ( !kernel_process )
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -149,10 +149,21 @@ LFBTextBuffer::~LFBTextBuffer()
|
||||||
{
|
{
|
||||||
if ( queue_thread )
|
if ( queue_thread )
|
||||||
{
|
{
|
||||||
TextBufferCmd cmd;
|
|
||||||
cmd.type = TEXTBUFCMD_EXIT;
|
|
||||||
IssueCommand(&cmd);
|
|
||||||
kthread_mutex_lock(&queue_lock);
|
kthread_mutex_lock(&queue_lock);
|
||||||
|
if ( queue_is_paused )
|
||||||
|
{
|
||||||
|
queue_is_paused = false;
|
||||||
|
exit_after_pause = true;
|
||||||
|
kthread_cond_signal(&queue_resume);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TextBufferCmd cmd;
|
||||||
|
cmd.type = TEXTBUFCMD_EXIT;
|
||||||
|
kthread_mutex_unlock(&queue_lock);
|
||||||
|
IssueCommand(&cmd);
|
||||||
|
kthread_mutex_lock(&queue_lock);
|
||||||
|
}
|
||||||
while ( queue_thread )
|
while ( queue_thread )
|
||||||
kthread_cond_wait(&queue_exit, &queue_lock);
|
kthread_cond_wait(&queue_exit, &queue_lock);
|
||||||
kthread_mutex_unlock(&queue_lock);
|
kthread_mutex_unlock(&queue_lock);
|
||||||
|
@ -163,12 +174,12 @@ LFBTextBuffer::~LFBTextBuffer()
|
||||||
delete[] queue;
|
delete[] queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t LFBTextBuffer::Width() const
|
size_t LFBTextBuffer::Width()
|
||||||
{
|
{
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t LFBTextBuffer::Height() const
|
size_t LFBTextBuffer::Height()
|
||||||
{
|
{
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
@ -363,16 +374,17 @@ void LFBTextBuffer::IssueCommand(TextBufferCmd* cmd)
|
||||||
queue[(queue_offset + queue_used++) % queue_length] = *cmd;
|
queue[(queue_offset + queue_used++) % queue_length] = *cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LFBTextBuffer::StopRendering()
|
bool LFBTextBuffer::StopRendering()
|
||||||
{
|
{
|
||||||
if ( !queue_thread || emergency_state )
|
if ( !queue_thread || emergency_state )
|
||||||
return;
|
return false;
|
||||||
TextBufferCmd cmd;
|
TextBufferCmd cmd;
|
||||||
cmd.type = TEXTBUFCMD_PAUSE;
|
cmd.type = TEXTBUFCMD_PAUSE;
|
||||||
IssueCommand(&cmd);
|
IssueCommand(&cmd);
|
||||||
ScopedLock lock(&queue_lock);
|
ScopedLock lock(&queue_lock);
|
||||||
while ( !queue_is_paused )
|
while ( !queue_is_paused )
|
||||||
kthread_cond_wait(&queue_paused, &queue_lock);
|
kthread_cond_wait(&queue_paused, &queue_lock);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LFBTextBuffer::ResumeRendering()
|
void LFBTextBuffer::ResumeRendering()
|
||||||
|
@ -380,17 +392,20 @@ void LFBTextBuffer::ResumeRendering()
|
||||||
if ( !queue_thread || emergency_state )
|
if ( !queue_thread || emergency_state )
|
||||||
return;
|
return;
|
||||||
ScopedLock lock(&queue_lock);
|
ScopedLock lock(&queue_lock);
|
||||||
|
if ( !queue_is_paused )
|
||||||
|
return;
|
||||||
queue_is_paused = false;
|
queue_is_paused = false;
|
||||||
kthread_cond_signal(&queue_resume);
|
kthread_cond_signal(&queue_resume);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextChar LFBTextBuffer::GetChar(TextPos pos) const
|
TextChar LFBTextBuffer::GetChar(TextPos pos)
|
||||||
{
|
{
|
||||||
if ( UsablePosition(pos) )
|
if ( UsablePosition(pos) )
|
||||||
{
|
{
|
||||||
((LFBTextBuffer*) this)->StopRendering();
|
bool was_rendering = StopRendering();
|
||||||
TextChar ret = chars[pos.y * columns + pos.x];
|
TextChar ret = chars[pos.y * columns + pos.x];
|
||||||
((LFBTextBuffer*) this)->ResumeRendering();
|
if ( was_rendering )
|
||||||
|
ResumeRendering();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return {0, 0, 0};
|
return {0, 0, 0};
|
||||||
|
@ -408,11 +423,12 @@ void LFBTextBuffer::SetChar(TextPos pos, TextChar c)
|
||||||
IssueCommand(&cmd);
|
IssueCommand(&cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LFBTextBuffer::GetCursorEnabled() const
|
bool LFBTextBuffer::GetCursorEnabled()
|
||||||
{
|
{
|
||||||
((LFBTextBuffer*) this)->StopRendering();
|
bool was_rendering = StopRendering();
|
||||||
bool ret = cursorenabled;
|
bool ret = cursorenabled;
|
||||||
((LFBTextBuffer*) this)->ResumeRendering();
|
if ( was_rendering )
|
||||||
|
ResumeRendering();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,11 +440,12 @@ void LFBTextBuffer::SetCursorEnabled(bool enablecursor)
|
||||||
IssueCommand(&cmd);
|
IssueCommand(&cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextPos LFBTextBuffer::GetCursorPos() const
|
TextPos LFBTextBuffer::GetCursorPos()
|
||||||
{
|
{
|
||||||
((LFBTextBuffer*) this)->StopRendering();
|
bool was_rendering = StopRendering();
|
||||||
TextPos ret = cursorpos;
|
TextPos ret = cursorpos;
|
||||||
((LFBTextBuffer*) this)->ResumeRendering();
|
if ( was_rendering )
|
||||||
|
ResumeRendering();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,6 +692,13 @@ void LFBTextBuffer::RenderThread()
|
||||||
while ( queue_is_paused )
|
while ( queue_is_paused )
|
||||||
kthread_cond_wait(&queue_resume, &queue_lock);
|
kthread_cond_wait(&queue_resume, &queue_lock);
|
||||||
pause_requested = false;
|
pause_requested = false;
|
||||||
|
if ( exit_after_pause )
|
||||||
|
{
|
||||||
|
queue_thread = false;
|
||||||
|
kthread_cond_signal(&queue_exit);
|
||||||
|
kthread_mutex_unlock(&queue_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,4 +785,20 @@ void LFBTextBuffer::EmergencyReset()
|
||||||
SetCursorPos(TextPos{0, 0});
|
SetCursorPos(TextPos{0, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LFBTextBuffer::Resume()
|
||||||
|
{
|
||||||
|
if ( need_clear )
|
||||||
|
{
|
||||||
|
for ( size_t y = 0; y < pixelsy; y++ )
|
||||||
|
memset(lfb + scansize * y, 0, bytes_per_pixel * pixelsx);
|
||||||
|
need_clear = false;
|
||||||
|
}
|
||||||
|
ResumeRendering();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LFBTextBuffer::Pause()
|
||||||
|
{
|
||||||
|
StopRendering();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -64,22 +64,24 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~LFBTextBuffer();
|
virtual ~LFBTextBuffer();
|
||||||
virtual size_t Width() const;
|
virtual size_t Width();
|
||||||
virtual size_t Height() const;
|
virtual size_t Height();
|
||||||
virtual TextChar GetChar(TextPos pos) const;
|
virtual TextChar GetChar(TextPos pos);
|
||||||
virtual void SetChar(TextPos pos, TextChar c);
|
virtual void SetChar(TextPos pos, TextChar c);
|
||||||
virtual void Scroll(ssize_t off, TextChar fillwith);
|
virtual void Scroll(ssize_t off, TextChar fillwith);
|
||||||
virtual void Move(TextPos to, TextPos from, size_t numchars);
|
virtual void Move(TextPos to, TextPos from, size_t numchars);
|
||||||
virtual void Fill(TextPos from, TextPos to, TextChar fillwith);
|
virtual void Fill(TextPos from, TextPos to, TextChar fillwith);
|
||||||
virtual bool GetCursorEnabled() const;
|
virtual bool GetCursorEnabled();
|
||||||
virtual void SetCursorEnabled(bool enablecursor);
|
virtual void SetCursorEnabled(bool enablecursor);
|
||||||
virtual TextPos GetCursorPos() const;
|
virtual TextPos GetCursorPos();
|
||||||
virtual void SetCursorPos(TextPos newcursorpos);
|
virtual void SetCursorPos(TextPos newcursorpos);
|
||||||
virtual void SpawnThreads();
|
virtual void SpawnThreads();
|
||||||
virtual void Invalidate();
|
virtual void Invalidate();
|
||||||
virtual bool EmergencyIsImpaired();
|
virtual bool EmergencyIsImpaired();
|
||||||
virtual bool EmergencyRecoup();
|
virtual bool EmergencyRecoup();
|
||||||
virtual void EmergencyReset();
|
virtual void EmergencyReset();
|
||||||
|
virtual void Resume();
|
||||||
|
virtual void Pause();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void RenderThread();
|
virtual void RenderThread();
|
||||||
|
@ -97,7 +99,7 @@ private:
|
||||||
void DoMove(TextPos to, TextPos from, size_t numchars);
|
void DoMove(TextPos to, TextPos from, size_t numchars);
|
||||||
void DoFill(TextPos from, TextPos to, TextChar fillwith);
|
void DoFill(TextPos from, TextPos to, TextChar fillwith);
|
||||||
void IssueCommand(TextBufferCmd* cmd);
|
void IssueCommand(TextBufferCmd* cmd);
|
||||||
void StopRendering();
|
bool StopRendering();
|
||||||
void ResumeRendering();
|
void ResumeRendering();
|
||||||
bool IsCommandIdempotent(const TextBufferCmd* cmd) const;
|
bool IsCommandIdempotent(const TextBufferCmd* cmd) const;
|
||||||
void ExecuteCommand(TextBufferCmd* cmd,
|
void ExecuteCommand(TextBufferCmd* cmd,
|
||||||
|
@ -138,6 +140,8 @@ private:
|
||||||
TextPos cursorpos;
|
TextPos cursorpos;
|
||||||
bool emergency_state;
|
bool emergency_state;
|
||||||
bool invalidated;
|
bool invalidated;
|
||||||
|
bool need_clear;
|
||||||
|
bool exit_after_pause;
|
||||||
size_t execute_amount;
|
size_t execute_amount;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -246,5 +246,20 @@ void Center(const char* string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BeginReplace()
|
||||||
|
{
|
||||||
|
((TextTerminal*) Log::device_pointer)->BeginReplace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CancelReplace()
|
||||||
|
{
|
||||||
|
((TextTerminal*) Log::device_pointer)->CancelReplace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishReplace(TextBuffer* textbuf)
|
||||||
|
{
|
||||||
|
((TextTerminal*) Log::device_pointer)->FinishReplace(textbuf);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Log
|
} // namespace Log
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2013, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2013, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -94,13 +94,77 @@ void TextBufferHandle::EmergencyRelease(TextBuffer* textbuf)
|
||||||
(void) textbuf;
|
(void) textbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBufferHandle::Replace(TextBuffer* newtextbuf)
|
void TextBufferHandle::BeginReplace()
|
||||||
{
|
{
|
||||||
ScopedLock lock(&mutex);
|
kthread_mutex_lock(&mutex);
|
||||||
while ( 0 < numused )
|
while ( 0 < numused )
|
||||||
kthread_cond_wait(&unusedcond, &mutex);
|
kthread_cond_wait(&unusedcond, &mutex);
|
||||||
|
if ( textbuf )
|
||||||
|
textbuf->Pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferHandle::CancelReplace()
|
||||||
|
{
|
||||||
|
if ( textbuf )
|
||||||
|
textbuf->Resume();
|
||||||
|
kthread_mutex_unlock(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextBufferHandle::FinishReplace(TextBuffer* newtextbuf)
|
||||||
|
{
|
||||||
|
// TODO: This shouldn't redraw when a graphical app is in control of the
|
||||||
|
// screen. Might even leak information from the console.
|
||||||
|
newtextbuf->Resume();
|
||||||
|
if ( textbuf )
|
||||||
|
{
|
||||||
|
size_t src_width = textbuf->Width();
|
||||||
|
size_t src_height = textbuf->Height();
|
||||||
|
size_t dst_width = newtextbuf->Width();
|
||||||
|
size_t dst_height = newtextbuf->Height();
|
||||||
|
bool cursor_enabled = textbuf->GetCursorEnabled();
|
||||||
|
TextPos src_cursor = textbuf->GetCursorPos();
|
||||||
|
size_t src_y_after_cursor = src_height ? src_cursor.y + 1 : 0;
|
||||||
|
size_t src_y_count = dst_height < src_y_after_cursor ? dst_height : src_y_after_cursor;
|
||||||
|
size_t src_y_from = src_y_after_cursor - src_y_count;
|
||||||
|
TextPos dst_cursor = src_cursor;
|
||||||
|
dst_cursor.y += src_y_from;
|
||||||
|
newtextbuf->SetCursorEnabled(false);
|
||||||
|
for ( size_t dst_y = 0; dst_y < dst_height; dst_y++ )
|
||||||
|
{
|
||||||
|
// TODO: Ability to center the boot cat.
|
||||||
|
size_t src_y = src_y_from + dst_y;
|
||||||
|
for ( size_t dst_x = 0; dst_x < dst_width; dst_x++ )
|
||||||
|
{
|
||||||
|
size_t src_x = dst_x;
|
||||||
|
TextPos src_pos{src_x, src_y};
|
||||||
|
TextPos dst_pos{dst_x, dst_y};
|
||||||
|
TextChar tc{0, 0, 0};
|
||||||
|
if ( src_x < src_width && src_y < src_height )
|
||||||
|
tc = textbuf->GetChar(src_pos);
|
||||||
|
else if ( src_width && src_height )
|
||||||
|
{
|
||||||
|
TextPos templ_pos;
|
||||||
|
templ_pos.x = src_y < src_width ? src_y : src_x- 1;
|
||||||
|
templ_pos.y = src_y < src_height ? src_y : src_height - 1;
|
||||||
|
tc = textbuf->GetChar(templ_pos);
|
||||||
|
tc.c = 0;
|
||||||
|
tc.attr = 0;
|
||||||
|
}
|
||||||
|
newtextbuf->SetChar(dst_pos, tc);
|
||||||
|
if ( src_x == src_cursor.x && src_y == src_cursor.y )
|
||||||
|
dst_cursor = dst_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( dst_width <= dst_cursor.x )
|
||||||
|
dst_cursor.x = dst_width ? dst_cursor.x - 1 : 0;
|
||||||
|
if ( dst_height <= dst_cursor.y )
|
||||||
|
dst_cursor.y = dst_height ? dst_cursor.y - 1 : 0;
|
||||||
|
newtextbuf->SetCursorPos(dst_cursor);
|
||||||
|
newtextbuf->SetCursorEnabled(cursor_enabled);
|
||||||
|
}
|
||||||
delete textbuf;
|
delete textbuf;
|
||||||
textbuf = newtextbuf;
|
textbuf = newtextbuf;
|
||||||
|
kthread_mutex_unlock(&mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -133,6 +133,32 @@ bool TextTerminal::Invalidate()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextTerminal::BeginReplace()
|
||||||
|
{
|
||||||
|
kthread_mutex_lock(&termlock);
|
||||||
|
textbufhandle->BeginReplace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextTerminal::CancelReplace()
|
||||||
|
{
|
||||||
|
textbufhandle->CancelReplace();
|
||||||
|
kthread_mutex_unlock(&termlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextTerminal::FinishReplace(TextBuffer* new_textbuf)
|
||||||
|
{
|
||||||
|
textbufhandle->FinishReplace(new_textbuf);
|
||||||
|
TextBuffer* textbuf = textbufhandle->Acquire();
|
||||||
|
size_t new_width = textbuf->Width();
|
||||||
|
size_t new_height = textbuf->Height();
|
||||||
|
textbufhandle->Release(textbuf);
|
||||||
|
if ( new_width < column )
|
||||||
|
column = new_width;
|
||||||
|
if ( new_height <= line )
|
||||||
|
line = new_height ? new_height - 1 : 0;
|
||||||
|
kthread_mutex_unlock(&termlock);
|
||||||
|
}
|
||||||
|
|
||||||
bool TextTerminal::EmergencyIsImpaired()
|
bool TextTerminal::EmergencyIsImpaired()
|
||||||
{
|
{
|
||||||
// This is during a kernel emergency where preemption has been disabled and
|
// This is during a kernel emergency where preemption has been disabled and
|
||||||
|
|
|
@ -41,6 +41,9 @@ public:
|
||||||
void GetCursor(size_t* column, size_t* row) const;
|
void GetCursor(size_t* column, size_t* row) const;
|
||||||
bool Sync();
|
bool Sync();
|
||||||
bool Invalidate();
|
bool Invalidate();
|
||||||
|
void BeginReplace();
|
||||||
|
void CancelReplace();
|
||||||
|
void FinishReplace(TextBuffer* textbuf);
|
||||||
bool EmergencyIsImpaired();
|
bool EmergencyIsImpaired();
|
||||||
bool EmergencyRecoup();
|
bool EmergencyRecoup();
|
||||||
void EmergencyReset();
|
void EmergencyReset();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -74,17 +74,17 @@ size_t VGATextBuffer::OffsetOfPos(TextPos pos) const
|
||||||
return pos.y * width + pos.x;
|
return pos.y * width + pos.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t VGATextBuffer::Width() const
|
size_t VGATextBuffer::Width()
|
||||||
{
|
{
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t VGATextBuffer::Height() const
|
size_t VGATextBuffer::Height()
|
||||||
{
|
{
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextChar VGATextBuffer::GetChar(TextPos pos) const
|
TextChar VGATextBuffer::GetChar(TextPos pos)
|
||||||
{
|
{
|
||||||
if ( UsablePosition(pos) )
|
if ( UsablePosition(pos) )
|
||||||
return chars[OffsetOfPos(pos)];
|
return chars[OffsetOfPos(pos)];
|
||||||
|
@ -153,7 +153,7 @@ void VGATextBuffer::Fill(TextPos from, TextPos to, TextChar fillwith)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VGATextBuffer::GetCursorEnabled() const
|
bool VGATextBuffer::GetCursorEnabled()
|
||||||
{
|
{
|
||||||
return cursorenabled;
|
return cursorenabled;
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ void VGATextBuffer::SetCursorEnabled(bool enablecursor)
|
||||||
UpdateCursor();
|
UpdateCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextPos VGATextBuffer::GetCursorPos() const
|
TextPos VGATextBuffer::GetCursorPos()
|
||||||
{
|
{
|
||||||
return cursorpos;
|
return cursorpos;
|
||||||
}
|
}
|
||||||
|
@ -207,4 +207,13 @@ void VGATextBuffer::EmergencyReset()
|
||||||
SetCursorPos(TextPos{0, 0});
|
SetCursorPos(TextPos{0, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VGATextBuffer::Resume()
|
||||||
|
{
|
||||||
|
UpdateCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VGATextBuffer::Pause()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Sortix
|
} // namespace Sortix
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -29,22 +29,24 @@ class VGATextBuffer : public TextBuffer
|
||||||
public:
|
public:
|
||||||
VGATextBuffer(uint16_t* vga, TextChar* chars, size_t width, size_t height);
|
VGATextBuffer(uint16_t* vga, TextChar* chars, size_t width, size_t height);
|
||||||
virtual ~VGATextBuffer();
|
virtual ~VGATextBuffer();
|
||||||
virtual size_t Width() const;
|
virtual size_t Width();
|
||||||
virtual size_t Height() const;
|
virtual size_t Height();
|
||||||
virtual TextChar GetChar(TextPos pos) const;
|
virtual TextChar GetChar(TextPos pos);
|
||||||
virtual void SetChar(TextPos pos, TextChar c);
|
virtual void SetChar(TextPos pos, TextChar c);
|
||||||
virtual void Scroll(ssize_t off, TextChar fillwith);
|
virtual void Scroll(ssize_t off, TextChar fillwith);
|
||||||
virtual void Move(TextPos to, TextPos from, size_t numchars);
|
virtual void Move(TextPos to, TextPos from, size_t numchars);
|
||||||
virtual void Fill(TextPos from, TextPos to, TextChar fillwith);
|
virtual void Fill(TextPos from, TextPos to, TextChar fillwith);
|
||||||
virtual bool GetCursorEnabled() const;
|
virtual bool GetCursorEnabled();
|
||||||
virtual void SetCursorEnabled(bool enablecursor);
|
virtual void SetCursorEnabled(bool enablecursor);
|
||||||
virtual TextPos GetCursorPos() const;
|
virtual TextPos GetCursorPos();
|
||||||
virtual void SetCursorPos(TextPos cursorpos);
|
virtual void SetCursorPos(TextPos cursorpos);
|
||||||
virtual void SpawnThreads();
|
virtual void SpawnThreads();
|
||||||
virtual void Invalidate();
|
virtual void Invalidate();
|
||||||
virtual bool EmergencyIsImpaired();
|
virtual bool EmergencyIsImpaired();
|
||||||
virtual bool EmergencyRecoup();
|
virtual bool EmergencyRecoup();
|
||||||
virtual void EmergencyReset();
|
virtual void EmergencyReset();
|
||||||
|
virtual void Resume();
|
||||||
|
virtual void Pause();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool UsablePosition(TextPos pos) const;
|
bool UsablePosition(TextPos pos) const;
|
||||||
|
|
127
kernel/video.cpp
127
kernel/video.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2014, 2015 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -33,10 +33,10 @@
|
||||||
namespace Sortix {
|
namespace Sortix {
|
||||||
namespace Video {
|
namespace Video {
|
||||||
|
|
||||||
const uint64_t ONE_AND_ONLY_DEVICE = 0;
|
static const uint64_t CONSOLE_DEVICE = 0;
|
||||||
const uint64_t ONE_AND_ONLY_CONNECTOR = 0;
|
static const uint64_t CONSOLE_CONNECTOR = 0;
|
||||||
|
|
||||||
kthread_mutex_t video_lock = KTHREAD_MUTEX_INITIALIZER;
|
static kthread_mutex_t video_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
struct DeviceEntry
|
struct DeviceEntry
|
||||||
{
|
{
|
||||||
|
@ -44,9 +44,9 @@ struct DeviceEntry
|
||||||
VideoDevice* device;
|
VideoDevice* device;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t num_devices = 0;
|
static size_t num_devices = 0;
|
||||||
size_t devices_length = 0;
|
static size_t devices_length = 0;
|
||||||
DeviceEntry* devices = NULL;
|
static DeviceEntry* devices = NULL;
|
||||||
|
|
||||||
bool RegisterDevice(const char* name, VideoDevice* device)
|
bool RegisterDevice(const char* name, VideoDevice* device)
|
||||||
{
|
{
|
||||||
|
@ -73,9 +73,89 @@ bool RegisterDevice(const char* name, VideoDevice* device)
|
||||||
size_t index = num_devices++;
|
size_t index = num_devices++;
|
||||||
devices[index].name = drivername;
|
devices[index].name = drivername;
|
||||||
devices[index].device = device;
|
devices[index].device = device;
|
||||||
|
device->device_index = index;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool SetVideoMode(VideoDevice* device,
|
||||||
|
uint64_t connector,
|
||||||
|
struct dispmsg_crtc_mode mode)
|
||||||
|
{
|
||||||
|
uint64_t device_index = device->device_index;
|
||||||
|
TextBuffer* textbuf = NULL;
|
||||||
|
if ( device_index == CONSOLE_DEVICE && connector == CONSOLE_CONNECTOR )
|
||||||
|
{
|
||||||
|
if ( !(textbuf = device->CreateTextBuffer(connector, mode)) )
|
||||||
|
return false;
|
||||||
|
Log::BeginReplace();
|
||||||
|
}
|
||||||
|
if ( !device->SwitchMode(connector, mode) )
|
||||||
|
{
|
||||||
|
if ( textbuf )
|
||||||
|
{
|
||||||
|
Log::CancelReplace();
|
||||||
|
delete textbuf;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( textbuf )
|
||||||
|
{
|
||||||
|
Log::FinishReplace(textbuf);
|
||||||
|
Log::fallback_framebuffer = NULL;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigureDevice(VideoDevice* device)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
ScopedLock lock(&video_lock);
|
||||||
|
uint64_t connectors_count = device->GetConnectorCount();
|
||||||
|
for ( uint64_t connector = 0; connector < connectors_count; connector++ )
|
||||||
|
{
|
||||||
|
struct dispmsg_crtc_mode mode;
|
||||||
|
if ( !device->GetDefaultMode(connector, &mode) ||
|
||||||
|
!SetVideoMode(device, connector, mode) )
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResizeDisplay(uint64_t device_index, uint64_t connector, uint32_t xres,
|
||||||
|
uint32_t yres, uint32_t bpp)
|
||||||
|
{
|
||||||
|
ScopedLock lock(&video_lock);
|
||||||
|
|
||||||
|
if ( num_devices <= device_index )
|
||||||
|
return errno = ENODEV, false;
|
||||||
|
|
||||||
|
DeviceEntry* device_entry = &devices[device_index];
|
||||||
|
VideoDevice* device = device_entry->device;
|
||||||
|
|
||||||
|
// TODO: xres/yres/bpp == 0 means unchanged, so get current mode.
|
||||||
|
if ( xres == 0 || yres == 0 || bpp == 0 )
|
||||||
|
return true;
|
||||||
|
struct dispmsg_crtc_mode mode;
|
||||||
|
memset(&mode, 0, sizeof(0));
|
||||||
|
mode.driver_index = 0;
|
||||||
|
mode.magic = 0;
|
||||||
|
mode.control = DISPMSG_CONTROL_VALID;
|
||||||
|
mode.fb_format = bpp;
|
||||||
|
mode.view_xres = xres;
|
||||||
|
mode.view_yres = yres;
|
||||||
|
mode.fb_location = 0;
|
||||||
|
mode.pitch = xres * (bpp + 7) / 8;
|
||||||
|
mode.surf_off_x = 0;
|
||||||
|
mode.surf_off_y = 0;
|
||||||
|
mode.start_x = 0;
|
||||||
|
mode.start_y = 0;
|
||||||
|
mode.end_x = 0;
|
||||||
|
mode.end_y = 0;
|
||||||
|
mode.desktop_height = yres;
|
||||||
|
|
||||||
|
return SetVideoMode(device, connector, mode);
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
static bool TransmitString(struct dispmsg_string* dest, const char* str)
|
static bool TransmitString(struct dispmsg_string* dest, const char* str)
|
||||||
{
|
{
|
||||||
|
@ -246,19 +326,13 @@ static int SetCrtcMode(void* ptr, size_t size)
|
||||||
{
|
{
|
||||||
DeviceEntry* device_entry = &devices[msg.device];
|
DeviceEntry* device_entry = &devices[msg.device];
|
||||||
VideoDevice* device = device_entry->device;
|
VideoDevice* device = device_entry->device;
|
||||||
if ( !device->SwitchMode(msg.connector, msg.mode) )
|
|
||||||
|
if ( !SetVideoMode(device, msg.connector, msg.mode) )
|
||||||
return -1;
|
return -1;
|
||||||
// TODO: This could potentially fail.
|
|
||||||
if ( msg.device == ONE_AND_ONLY_DEVICE &&
|
|
||||||
msg.connector == ONE_AND_ONLY_CONNECTOR )
|
|
||||||
{
|
|
||||||
Log::fallback_framebuffer = NULL;
|
|
||||||
Log::device_textbufhandle->Replace(device->CreateTextBuffer(msg.connector));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ( Log::fallback_framebuffer &&
|
else if ( Log::fallback_framebuffer &&
|
||||||
msg.device == ONE_AND_ONLY_DEVICE &&
|
msg.device == CONSOLE_DEVICE &&
|
||||||
msg.connector == ONE_AND_ONLY_CONNECTOR )
|
msg.connector == CONSOLE_CONNECTOR )
|
||||||
{
|
{
|
||||||
struct dispmsg_crtc_mode fallback_mode = GetLogFallbackMode();
|
struct dispmsg_crtc_mode fallback_mode = GetLogFallbackMode();
|
||||||
if ( memcmp(&msg.mode, &fallback_mode, sizeof(msg.mode)) != 0 )
|
if ( memcmp(&msg.mode, &fallback_mode, sizeof(msg.mode)) != 0 )
|
||||||
|
@ -286,8 +360,8 @@ static int GetCrtcMode(void* ptr, size_t size)
|
||||||
|
|
||||||
struct dispmsg_crtc_mode mode;
|
struct dispmsg_crtc_mode mode;
|
||||||
if ( Log::fallback_framebuffer &&
|
if ( Log::fallback_framebuffer &&
|
||||||
msg.device == ONE_AND_ONLY_DEVICE &&
|
msg.device == CONSOLE_DEVICE &&
|
||||||
msg.connector == ONE_AND_ONLY_CONNECTOR )
|
msg.connector == CONSOLE_CONNECTOR )
|
||||||
{
|
{
|
||||||
mode = GetLogFallbackMode();
|
mode = GetLogFallbackMode();
|
||||||
}
|
}
|
||||||
|
@ -295,10 +369,7 @@ static int GetCrtcMode(void* ptr, size_t size)
|
||||||
{
|
{
|
||||||
DeviceEntry* device_entry = &devices[msg.device];
|
DeviceEntry* device_entry = &devices[msg.device];
|
||||||
VideoDevice* device = device_entry->device;
|
VideoDevice* device = device_entry->device;
|
||||||
// TODO: There is no real way to detect failure here.
|
if ( !device->GetCurrentMode(msg.connector, &mode) )
|
||||||
errno = 0;
|
|
||||||
mode = device->GetCurrentMode(msg.connector);
|
|
||||||
if ( !(mode.control & DISPMSG_CONTROL_VALID) && errno != 0 )
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -335,8 +406,8 @@ static int GetCrtcModes(void* ptr, size_t size)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else if ( Log::fallback_framebuffer &&
|
else if ( Log::fallback_framebuffer &&
|
||||||
msg.device == ONE_AND_ONLY_DEVICE &&
|
msg.device == CONSOLE_DEVICE &&
|
||||||
msg.connector == ONE_AND_ONLY_CONNECTOR )
|
msg.connector == CONSOLE_CONNECTOR )
|
||||||
{
|
{
|
||||||
if ( !(modes = new struct dispmsg_crtc_mode[1]) )
|
if ( !(modes = new struct dispmsg_crtc_mode[1]) )
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -384,7 +455,7 @@ static int GetMemorySize(void* ptr, size_t size)
|
||||||
ScopedLock lock(&video_lock);
|
ScopedLock lock(&video_lock);
|
||||||
|
|
||||||
if ( Log::fallback_framebuffer &&
|
if ( Log::fallback_framebuffer &&
|
||||||
msg.device == ONE_AND_ONLY_DEVICE )
|
msg.device == CONSOLE_DEVICE )
|
||||||
{
|
{
|
||||||
msg.memory_size = Log::fallback_framebuffer_width *
|
msg.memory_size = Log::fallback_framebuffer_width *
|
||||||
Log::fallback_framebuffer_height *
|
Log::fallback_framebuffer_height *
|
||||||
|
@ -423,7 +494,7 @@ static int WriteMemory(void* ptr, size_t size)
|
||||||
ScopedLock lock(&video_lock);
|
ScopedLock lock(&video_lock);
|
||||||
|
|
||||||
if ( Log::fallback_framebuffer &&
|
if ( Log::fallback_framebuffer &&
|
||||||
msg.device == ONE_AND_ONLY_DEVICE )
|
msg.device == CONSOLE_DEVICE )
|
||||||
{
|
{
|
||||||
size_t ideal_pitch = Log::fallback_framebuffer_width *
|
size_t ideal_pitch = Log::fallback_framebuffer_width *
|
||||||
Log::fallback_framebuffer_bpp / 8;
|
Log::fallback_framebuffer_bpp / 8;
|
||||||
|
@ -493,7 +564,7 @@ static int ReadMemory(void* ptr, size_t size)
|
||||||
ScopedLock lock(&video_lock);
|
ScopedLock lock(&video_lock);
|
||||||
|
|
||||||
if ( Log::fallback_framebuffer &&
|
if ( Log::fallback_framebuffer &&
|
||||||
msg.device == ONE_AND_ONLY_DEVICE )
|
msg.device == CONSOLE_DEVICE )
|
||||||
{
|
{
|
||||||
size_t ideal_pitch = Log::fallback_framebuffer_width *
|
size_t ideal_pitch = Log::fallback_framebuffer_width *
|
||||||
Log::fallback_framebuffer_bpp / 8;
|
Log::fallback_framebuffer_bpp / 8;
|
||||||
|
|
|
@ -544,10 +544,28 @@ int main(void)
|
||||||
(dgdn.device = display.device, true) &&
|
(dgdn.device = display.device, true) &&
|
||||||
(dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) )
|
(dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) )
|
||||||
{
|
{
|
||||||
|
struct dispmsg_get_crtc_mode get_mode;
|
||||||
|
memset(&get_mode, 0, sizeof(get_mode));
|
||||||
|
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
||||||
|
get_mode.device = 0;
|
||||||
|
get_mode.connector = 0;
|
||||||
|
bool good = false;
|
||||||
|
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) == 0 )
|
||||||
|
{
|
||||||
|
good = (get_mode.mode.control & DISPMSG_CONTROL_VALID) &&
|
||||||
|
(get_mode.mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||||
|
if ( get_mode.mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||||
|
{
|
||||||
|
text("The display resolution will automatically change to "
|
||||||
|
"match the size of the virtual machine window.\n\n");
|
||||||
|
good = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char* def = good ? "no" : "yes";
|
||||||
while ( true )
|
while ( true )
|
||||||
{
|
{
|
||||||
prompt(input, sizeof(input),
|
prompt(input, sizeof(input),
|
||||||
"Select a default display resolution? (yes/no)", "yes");
|
"Select a default display resolution? (yes/no)", def);
|
||||||
if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
|
if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
|
||||||
continue;
|
continue;
|
||||||
bool was_no = strcasecmp(input, "no") == 0;
|
bool was_no = strcasecmp(input, "no") == 0;
|
||||||
|
@ -556,10 +574,6 @@ int main(void)
|
||||||
break;
|
break;
|
||||||
if ( execute((const char*[]) { "chvideomode", NULL }, "f") != 0 )
|
if ( execute((const char*[]) { "chvideomode", NULL }, "f") != 0 )
|
||||||
continue;
|
continue;
|
||||||
struct dispmsg_get_crtc_mode get_mode;
|
|
||||||
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
|
||||||
get_mode.device = 0;
|
|
||||||
get_mode.connector = 0;
|
|
||||||
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) < 0 )
|
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) < 0 )
|
||||||
break;
|
break;
|
||||||
if ( !(get_mode.mode.control & DISPMSG_CONTROL_VALID) )
|
if ( !(get_mode.mode.control & DISPMSG_CONTROL_VALID) )
|
||||||
|
|
|
@ -439,10 +439,28 @@ int main(void)
|
||||||
(dgdn.device = display.device, true) &&
|
(dgdn.device = display.device, true) &&
|
||||||
(dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) )
|
(dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) )
|
||||||
{
|
{
|
||||||
|
struct dispmsg_get_crtc_mode get_mode;
|
||||||
|
memset(&get_mode, 0, sizeof(get_mode));
|
||||||
|
get_mode.msgid = DISPMSG_GET_CRTC_MODE;
|
||||||
|
get_mode.device = 0;
|
||||||
|
get_mode.connector = 0;
|
||||||
|
bool good = false;
|
||||||
|
if ( dispmsg_issue(&get_mode, sizeof(get_mode)) == 0 )
|
||||||
|
{
|
||||||
|
good = (get_mode.mode.control & DISPMSG_CONTROL_VALID) &&
|
||||||
|
(get_mode.mode.control & DISPMSG_CONTROL_GOOD_DEFAULT);
|
||||||
|
if ( get_mode.mode.control & DISPMSG_CONTROL_VM_AUTO_SCALE )
|
||||||
|
{
|
||||||
|
text("The display resolution will automatically change to "
|
||||||
|
"match the size of the virtual machine window.\n\n");
|
||||||
|
good = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char* def = good ? "no" : "yes";
|
||||||
while ( true )
|
while ( true )
|
||||||
{
|
{
|
||||||
prompt(input, sizeof(input),
|
prompt(input, sizeof(input),
|
||||||
"Select display resolution? (yes/no)", "yes");
|
"Select display resolution? (yes/no)", def);
|
||||||
if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
|
if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
|
||||||
continue;
|
continue;
|
||||||
if ( strcasecmp(input, "no") == 0 )
|
if ( strcasecmp(input, "no") == 0 )
|
||||||
|
|
Loading…
Reference in New Issue