2026-03-01 17:03:49 +08:00
|
|
|
using Cosmos.System;
|
|
|
|
|
using Cosmos.System.Graphics;
|
|
|
|
|
using CMLeonOS;
|
|
|
|
|
using CMLeonOS.Gui.ShellComponents;
|
2026-03-03 21:25:01 +08:00
|
|
|
using CMLeonOS.Settings;
|
2026-03-01 17:03:49 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
|
|
|
|
namespace CMLeonOS.Gui
|
|
|
|
|
{
|
|
|
|
|
internal class WindowManager : Process
|
|
|
|
|
{
|
|
|
|
|
internal WindowManager() : base("WindowManager", ProcessType.Service)
|
|
|
|
|
{
|
|
|
|
|
Critical = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Cosmos.HAL.Drivers.Video.SVGAII.VMWareSVGAII driver;
|
|
|
|
|
|
|
|
|
|
internal List<Window> Windows = new List<Window>();
|
|
|
|
|
|
|
|
|
|
internal Queue<Window> UpdateQueue = new Queue<Window>();
|
|
|
|
|
|
|
|
|
|
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.Cursor.bmp")]
|
|
|
|
|
private static byte[] cursorBytes;
|
|
|
|
|
private static Bitmap cursorBitmap = new Bitmap(cursorBytes);
|
|
|
|
|
|
|
|
|
|
/*[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.WaitCursor.bmp")]
|
|
|
|
|
private static byte[] waitCursorBytes;
|
|
|
|
|
private static Bitmap waitCursorBitmap = new Bitmap(waitCursorBytes);*/
|
|
|
|
|
|
|
|
|
|
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.Wallpaper_1280_800.bmp")]
|
|
|
|
|
private static byte[] wallpaperBytes;
|
|
|
|
|
private static Bitmap wallpaperBitmap = new Bitmap(wallpaperBytes);
|
|
|
|
|
|
|
|
|
|
internal uint ScreenWidth { get; private set; }
|
|
|
|
|
internal uint ScreenHeight { get; private set; }
|
|
|
|
|
|
|
|
|
|
internal Window Focus { get; set; }
|
|
|
|
|
|
|
|
|
|
private uint bytesPerPixel { get; set; }
|
|
|
|
|
|
|
|
|
|
private bool bufferModified = false;
|
|
|
|
|
|
|
|
|
|
private DateTime lastClickDate = DateTime.MinValue;
|
|
|
|
|
private TimeSpan doubleClickTimeSpan = TimeSpan.FromSeconds(1);
|
|
|
|
|
|
|
|
|
|
private Window fpsCounter;
|
|
|
|
|
private int fps;
|
|
|
|
|
private int framesThisSecond;
|
|
|
|
|
private int lastSecond;
|
|
|
|
|
|
|
|
|
|
private bool fpsShown = true;
|
|
|
|
|
|
|
|
|
|
private MouseState lastMouseState = MouseState.None;
|
|
|
|
|
private uint lastMouseX = 0;
|
|
|
|
|
private uint lastMouseY = 0;
|
|
|
|
|
|
|
|
|
|
private int sweepCounter = 0;
|
|
|
|
|
|
|
|
|
|
private Bitmap wallpaperResized;
|
|
|
|
|
|
|
|
|
|
internal int Fps
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return fps;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal List<Mode> AvailableModes { get; } = new List<Mode>
|
|
|
|
|
{
|
|
|
|
|
/* SD Resolutions */
|
|
|
|
|
/*new Mode(320, 200, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(320, 240, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(640, 480, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(720, 480, ColorDepth.ColorDepth32),*/
|
|
|
|
|
new Mode(800, 600, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(1024, 768, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(1152, 768, ColorDepth.ColorDepth32),
|
|
|
|
|
|
|
|
|
|
/* Old HD-Ready Resolutions */
|
|
|
|
|
new Mode(1280, 720, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(1280, 768, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(1280, 800, ColorDepth.ColorDepth32), // WXGA
|
|
|
|
|
new Mode(1280, 1024, ColorDepth.ColorDepth32), // SXGA
|
|
|
|
|
|
|
|
|
|
/* Better HD-Ready Resolutions */
|
|
|
|
|
new Mode(1360, 768, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(1440, 900, ColorDepth.ColorDepth32), // WXGA+
|
|
|
|
|
new Mode(1400, 1050, ColorDepth.ColorDepth32), // SXGA+
|
|
|
|
|
new Mode(1600, 1200, ColorDepth.ColorDepth32), // UXGA
|
|
|
|
|
new Mode(1680, 1050, ColorDepth.ColorDepth32), // WXGA++
|
|
|
|
|
|
|
|
|
|
/* HDTV Resolutions */
|
|
|
|
|
new Mode(1920, 1080, ColorDepth.ColorDepth32),
|
|
|
|
|
new Mode(1920, 1200, ColorDepth.ColorDepth32), // WUXGA
|
|
|
|
|
|
|
|
|
|
/* 2K Resolutions */
|
|
|
|
|
/*new Mode(2048, 1536, ColorDepth.ColorDepth32), // QXGA
|
|
|
|
|
new Mode(2560, 1080, ColorDepth.ColorDepth32), // UW-UXGA
|
|
|
|
|
new Mode(2560, 1600, ColorDepth.ColorDepth32), // WQXGA
|
|
|
|
|
new Mode(2560, 2048, ColorDepth.ColorDepth32), // QXGA+
|
|
|
|
|
new Mode(3200, 2048, ColorDepth.ColorDepth32), // WQXGA+
|
|
|
|
|
new Mode(3200, 2400, ColorDepth.ColorDepth32), // QUXGA
|
|
|
|
|
new Mode(3840, 2400, ColorDepth.ColorDepth32), // WQUXGA*/
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private void RenderWindow(Window window)
|
|
|
|
|
{
|
|
|
|
|
bufferModified = true;
|
|
|
|
|
|
|
|
|
|
int screenX = window.ScreenX;
|
|
|
|
|
int screenY = window.ScreenY;
|
|
|
|
|
int height = (int)Math.Min(window.Height, ScreenHeight - screenY);
|
|
|
|
|
int width = (int)Math.Min(window.Width, ScreenWidth - screenX);
|
|
|
|
|
|
|
|
|
|
uint index = (uint)((screenY * ScreenWidth) + screenX);
|
|
|
|
|
int byteOffset = (int)((index * bytesPerPixel) + driver.FrameSize);
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < height; y++)
|
|
|
|
|
{
|
|
|
|
|
int sourceIndex = y * window.Width;
|
|
|
|
|
driver.videoMemory.Copy(aByteOffset: byteOffset, aData: window.Buffer, aIndex: sourceIndex, aCount: width);
|
|
|
|
|
byteOffset += (int)(ScreenWidth * bytesPerPixel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateAbove(Window window)
|
|
|
|
|
{
|
|
|
|
|
Rectangle aRect = new Rectangle(window.ScreenX, window.ScreenY, window.Width, window.Height);
|
|
|
|
|
int aboveIndex = Windows.IndexOf(window) + 1;
|
|
|
|
|
for (int i = aboveIndex; i < Windows.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
Window bWindow = Windows[i];
|
|
|
|
|
Rectangle bRect = new Rectangle(bWindow.ScreenX, bWindow.ScreenY, bWindow.Width, bWindow.Height);
|
|
|
|
|
if (aRect.IntersectsWith(bRect))
|
|
|
|
|
{
|
|
|
|
|
RenderWindow(bWindow);
|
|
|
|
|
aRect = Rectangle.Union(aRect, bRect);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void Update(Window window)
|
|
|
|
|
{
|
|
|
|
|
if (!Windows.Contains(window)) return;
|
|
|
|
|
RenderWindow(window);
|
|
|
|
|
UpdateAbove(window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void RerenderAll()
|
|
|
|
|
{
|
|
|
|
|
RenderWallpaper();
|
|
|
|
|
|
|
|
|
|
foreach (Window window in Windows)
|
|
|
|
|
{
|
|
|
|
|
RenderWindow(window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void AddWindow(Window window)
|
|
|
|
|
{
|
|
|
|
|
if (Windows.Contains(window)) return;
|
|
|
|
|
|
|
|
|
|
Windows.Add(window);
|
|
|
|
|
|
|
|
|
|
UpdateDock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void RemoveWindow(Window window, bool rerender = true)
|
|
|
|
|
{
|
|
|
|
|
Windows.Remove(window);
|
|
|
|
|
for (int i = Windows.Count - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
if (i >= Windows.Count) continue;
|
|
|
|
|
if (Windows[i].RelativeTo == window)
|
|
|
|
|
{
|
|
|
|
|
RemoveWindow(Windows[i], rerender: false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rerender)
|
|
|
|
|
{
|
|
|
|
|
RerenderAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateDock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void ClearAllWindows()
|
|
|
|
|
{
|
|
|
|
|
for (int i = Windows.Count - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
if (i >= Windows.Count) continue;
|
|
|
|
|
if (Windows[i].Process != this)
|
|
|
|
|
{
|
|
|
|
|
if (Windows[i] is UILib.AppWindow appWindow)
|
|
|
|
|
{
|
|
|
|
|
appWindow.Closing?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
RemoveWindow(Windows[i], rerender: false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RerenderAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateDock()
|
|
|
|
|
{
|
|
|
|
|
var dock = ShellComponents.Dock.Dock.CurrentDock;
|
|
|
|
|
|
|
|
|
|
if (dock != null)
|
|
|
|
|
{
|
|
|
|
|
dock.UpdateWindows();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetupDriver()
|
|
|
|
|
{
|
|
|
|
|
driver = new Cosmos.HAL.Drivers.Video.SVGAII.VMWareSVGAII();
|
|
|
|
|
driver.SetMode(ScreenWidth, ScreenHeight, depth: bytesPerPixel * 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetupMouse()
|
|
|
|
|
{
|
|
|
|
|
MouseManager.ScreenWidth = ScreenWidth;
|
|
|
|
|
MouseManager.ScreenHeight = ScreenHeight;
|
|
|
|
|
|
|
|
|
|
MouseManager.X = ScreenWidth / 2;
|
|
|
|
|
MouseManager.Y = ScreenHeight / 2;
|
|
|
|
|
|
|
|
|
|
driver.DefineAlphaCursor(cursorBitmap.Width, cursorBitmap.Height, cursorBitmap.RawData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Window GetWindowAtPos(uint x, uint y)
|
|
|
|
|
{
|
|
|
|
|
for (int i = Windows.Count - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
Window window = Windows[i];
|
|
|
|
|
if (x >= window.ScreenX
|
|
|
|
|
&& y >= window.ScreenY
|
|
|
|
|
&& x < window.ScreenX + window.Width
|
|
|
|
|
&& y < window.ScreenY + window.Height)
|
|
|
|
|
{
|
|
|
|
|
return window;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DispatchEvents()
|
|
|
|
|
{
|
|
|
|
|
Window window = GetWindowAtPos(MouseManager.X, MouseManager.Y);
|
|
|
|
|
|
|
|
|
|
if (window != null)
|
|
|
|
|
{
|
|
|
|
|
int relativeX = (int)(MouseManager.X - window.ScreenX);
|
|
|
|
|
int relativeY = (int)(MouseManager.Y - window.ScreenY);
|
|
|
|
|
|
|
|
|
|
if (MouseManager.MouseState == MouseState.Left && lastMouseState == MouseState.None)
|
|
|
|
|
{
|
|
|
|
|
lastMouseState = MouseManager.MouseState;
|
|
|
|
|
window.OnDown?.Invoke(relativeX, relativeY);
|
|
|
|
|
}
|
|
|
|
|
else if (MouseManager.MouseState == MouseState.None && lastMouseState == MouseState.Left)
|
|
|
|
|
{
|
|
|
|
|
lastMouseState = MouseManager.MouseState;
|
|
|
|
|
|
|
|
|
|
if (Focus != null && Focus != window)
|
|
|
|
|
{
|
|
|
|
|
Focus.OnUnfocused?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
Focus = window;
|
|
|
|
|
window.OnFocused?.Invoke();
|
|
|
|
|
|
|
|
|
|
window.OnClick?.Invoke(relativeX, relativeY);
|
|
|
|
|
|
|
|
|
|
if (DateTime.Now - lastClickDate < doubleClickTimeSpan)
|
|
|
|
|
{
|
|
|
|
|
window.OnDoubleClick?.Invoke(relativeX, relativeY);
|
|
|
|
|
}
|
|
|
|
|
lastClickDate = DateTime.Now;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (KeyboardManager.TryReadKey(out KeyEvent key))
|
|
|
|
|
{
|
|
|
|
|
// To-do: Move this out of WindowManager.
|
|
|
|
|
if (key.Key == ConsoleKeyEx.LWin || key.Key == ConsoleKeyEx.RWin)
|
|
|
|
|
{
|
|
|
|
|
StartMenu.CurrentStartMenu?.ToggleStartMenu(focusSearch: true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Focus?.OnKeyPressed?.Invoke(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateFps()
|
|
|
|
|
{
|
|
|
|
|
framesThisSecond++;
|
|
|
|
|
int second = DateTime.Now.Second;
|
|
|
|
|
if (second != lastSecond)
|
|
|
|
|
{
|
|
|
|
|
fps = framesThisSecond;
|
|
|
|
|
framesThisSecond = 0;
|
|
|
|
|
|
|
|
|
|
if (fpsShown)
|
|
|
|
|
{
|
|
|
|
|
fpsCounter.Clear(Color.Black);
|
|
|
|
|
fpsCounter.DrawString($"{fps.ToString()} FPS", Color.White, 0, 0);
|
|
|
|
|
Update(fpsCounter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lastSecond = second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void HideFps()
|
|
|
|
|
{
|
|
|
|
|
fpsShown = false;
|
|
|
|
|
RemoveWindow(fpsCounter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void ShowFps()
|
|
|
|
|
{
|
|
|
|
|
fpsShown = true;
|
|
|
|
|
AddWindow(fpsCounter);
|
|
|
|
|
Update(fpsCounter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RenderWallpaper()
|
|
|
|
|
{
|
|
|
|
|
driver.videoMemory.Copy((int)driver.FrameSize, wallpaperResized.RawData, 0, wallpaperResized.RawData.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetupWallpaper()
|
|
|
|
|
{
|
|
|
|
|
wallpaperResized = wallpaperBitmap.Resize(ScreenWidth, ScreenHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateCursor()
|
|
|
|
|
{
|
|
|
|
|
uint mouseX = MouseManager.X;
|
|
|
|
|
uint mouseY = MouseManager.Y;
|
|
|
|
|
if (mouseX != lastMouseX || mouseY != lastMouseY)
|
|
|
|
|
{
|
|
|
|
|
driver.SetCursor(true, mouseX, mouseY);
|
|
|
|
|
}
|
|
|
|
|
lastMouseX = mouseX;
|
|
|
|
|
lastMouseY = mouseY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Sweep()
|
|
|
|
|
{
|
|
|
|
|
if (sweepCounter == 10)
|
|
|
|
|
{
|
|
|
|
|
sweepCounter = 0;
|
|
|
|
|
foreach (Window window in Windows)
|
|
|
|
|
{
|
|
|
|
|
if (window.Process != null && !window.Process.IsRunning)
|
|
|
|
|
{
|
|
|
|
|
RemoveWindow(window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sweepCounter++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Process
|
|
|
|
|
public override void Start()
|
|
|
|
|
{
|
|
|
|
|
base.Start();
|
|
|
|
|
|
|
|
|
|
SettingsService settingsService = ProcessManager.GetProcess<SettingsService>();
|
|
|
|
|
|
|
|
|
|
ScreenWidth = (uint)settingsService.Mode.Width;
|
|
|
|
|
ScreenHeight = (uint)settingsService.Mode.Height;
|
|
|
|
|
bytesPerPixel = 4;
|
|
|
|
|
|
|
|
|
|
SetupDriver();
|
|
|
|
|
SetupMouse();
|
|
|
|
|
SetupWallpaper();
|
|
|
|
|
RenderWallpaper();
|
|
|
|
|
|
|
|
|
|
fpsCounter = new Window(this, (int)(ScreenWidth) - 64, (int)(ScreenHeight - 16), 64, 16);
|
2026-03-03 21:25:01 +08:00
|
|
|
if (SettingsManager.GUI_ShowFps)
|
2026-03-01 17:03:49 +08:00
|
|
|
{
|
|
|
|
|
AddWindow(fpsCounter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Run()
|
|
|
|
|
{
|
|
|
|
|
UpdateFps();
|
|
|
|
|
|
|
|
|
|
Sweep();
|
|
|
|
|
|
|
|
|
|
if (UpdateQueue.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
Window toUpdate = UpdateQueue.Dequeue();
|
|
|
|
|
|
|
|
|
|
if (Windows.Contains(toUpdate))
|
|
|
|
|
{
|
|
|
|
|
if (toUpdate is UILib.Control control)
|
|
|
|
|
{
|
|
|
|
|
control.Render();
|
|
|
|
|
}
|
|
|
|
|
RenderWindow(toUpdate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DispatchEvents();
|
|
|
|
|
|
|
|
|
|
if (bufferModified)
|
|
|
|
|
{
|
|
|
|
|
driver.DoubleBufferUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateCursor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Stop()
|
|
|
|
|
{
|
|
|
|
|
base.Stop();
|
|
|
|
|
|
|
|
|
|
driver.Disable();
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|