Compare commits

...

1 Commits

Author SHA1 Message Date
LGUG2Z
f36926cdb1 feat(borders): wip 2024-05-10 22:17:51 -07:00
12 changed files with 648 additions and 3 deletions

28
Cargo.lock generated
View File

@@ -796,6 +796,33 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "komoborders"
version = "0.1.0"
dependencies = [
"color-eyre",
"dirs",
"komoborders-client",
"komorebi",
"komorebi-client",
"lazy_static",
"parking_lot",
"serde",
"serde_json",
"uds_windows",
"windows 0.54.0",
]
[[package]]
name = "komoborders-client"
version = "0.1.0"
dependencies = [
"dirs",
"serde",
"serde_json_lenient",
"uds_windows",
]
[[package]]
name = "komorebi"
version = "0.1.26-dev.0"
@@ -810,6 +837,7 @@ dependencies = [
"getset",
"hex_color",
"hotwatch",
"komoborders-client",
"komorebi-core",
"lazy_static",
"miow",

View File

@@ -2,7 +2,7 @@
resolver = "2"
members = [
"derive-ahk",
"derive-ahk", "komoborders", "komoborders-client",
"komorebi",
"komorebi-client",
"komorebi-core",
@@ -18,6 +18,8 @@ dirs = "5"
color-eyre = "0.6"
serde_json = { package = "serde_json_lenient", version = "0.1" }
sysinfo = "0.30"
serde = { version = "1", features = ["derive"] }
uds_windows = "1"
[workspace.dependencies.windows]
version = "0.54"

View File

@@ -0,0 +1,12 @@
[package]
name = "komoborders-client"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
uds_windows = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true }
dirs = { workspace = true }

View File

@@ -0,0 +1,74 @@
use serde::Deserialize;
use serde::Serialize;
use std::io::Write;
use std::str::FromStr;
use uds_windows::UnixStream;
const KOMOBORDERS: &str = "komoborders.sock";
pub fn send_message(message: &SocketMessage) -> std::io::Result<()> {
let socket = dirs::data_local_dir()
.expect("there is no local data directory")
.join("komorebi")
.join(KOMOBORDERS);
let mut connected = false;
while !connected {
if let Ok(mut stream) = UnixStream::connect(&socket) {
connected = true;
stream.write_all(serde_json::to_string(message)?.as_bytes())?;
}
}
Ok(())
}
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum ZOrder {
Top,
NoTopMost,
Bottom,
TopMost,
}
// impl From<isize> for ZOrder {
// fn from(value: isize) -> Self {
// match value {
// -2 => Self::NoTopMost,
// -1 => Self::TopMost,
// 0 => Self::Top,
// 1 => Self::Bottom,
// _ => unimplemented!(),
// }
// }
// }
impl Into<isize> for ZOrder {
fn into(self) -> isize {
match self {
ZOrder::Top => 0,
ZOrder::NoTopMost => -2,
ZOrder::Bottom => 1,
ZOrder::TopMost => -1,
}
}
}
#[derive(Serialize, Deserialize)]
pub enum SocketMessage {
FocusedColour(u32, u32, u32),
UnfocusedColour(u32, u32, u32),
MonocleColour(u32, u32, u32),
StackColour(u32, u32, u32),
Width(i32),
Offset(i32),
ZOrder(ZOrder),
}
impl FromStr for SocketMessage {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}

19
komoborders/Cargo.toml Normal file
View File

@@ -0,0 +1,19 @@
[package]
name = "komoborders"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
komorebi-client = { path = "../komorebi-client" }
komoborders-client = { path = "../komoborders-client" }
komorebi = { path = "../komorebi" }
serde_json = "1"
color-eyre = "0.6"
windows = { workspace = true }
lazy_static = "1"
parking_lot = "0.12"
uds_windows = { workspace = true }
serde = { workspace = true }
dirs = { workspace = true }

199
komoborders/src/border.rs Normal file
View File

@@ -0,0 +1,199 @@
use komoborders_client::ZOrder;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::mpsc;
use std::sync::Arc;
use std::time::Duration;
use windows::core::PCWSTR;
use windows::Win32::Foundation::COLORREF;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::LRESULT;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Gdi::BeginPaint;
use windows::Win32::Graphics::Gdi::CreatePen;
use windows::Win32::Graphics::Gdi::EndPaint;
use windows::Win32::Graphics::Gdi::InvalidateRect;
use windows::Win32::Graphics::Gdi::Rectangle;
use windows::Win32::Graphics::Gdi::SelectObject;
use windows::Win32::Graphics::Gdi::ValidateRect;
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
use windows::Win32::Graphics::Gdi::PS_INSIDEFRAME;
use windows::Win32::Graphics::Gdi::PS_SOLID;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
use crate::FocusKind;
use crate::FOCUSED_STATE;
use crate::RECT_STATE;
use komorebi::Rgb;
use komorebi::WindowsApi;
use komorebi_client::Rect;
pub static TRANSPARENCY: u32 = 0;
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(8);
pub static BORDER_OFFSET: AtomicI32 = AtomicI32::new(-1);
lazy_static! {
pub static ref Z_ORDER: Arc<Mutex<ZOrder>> = Arc::new(Mutex::new(ZOrder::Bottom));
pub static ref FOCUSED: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
Rgb::new(66, 165, 245)
)));
pub static ref UNFOCUSED: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
Rgb::new(128, 128, 128)
)));
pub static ref MONOCLE: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
Rgb::new(255, 51, 153)
)));
pub static ref STACK: AtomicU32 = AtomicU32::new(u32::from(komorebi_client::Colour::Rgb(
Rgb::new(0, 165, 66)
)));
}
pub struct Border {
pub hwnd: isize,
}
impl Border {
pub const fn hwnd(&self) -> HWND {
HWND(self.hwnd)
}
pub fn create(id: &str) -> color_eyre::Result<Self> {
let name: Vec<u16> = format!("komoborder-{id}\0").encode_utf16().collect();
let class_name = PCWSTR(name.as_ptr());
let h_module = WindowsApi::module_handle_w()?;
let window_class = WNDCLASSW {
hInstance: h_module.into(),
lpszClassName: class_name,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(Self::callback),
hbrBackground: WindowsApi::create_solid_brush(TRANSPARENCY),
..Default::default()
};
let _ = WindowsApi::register_class_w(&window_class);
let (hwnd_sender, hwnd_receiver) = mpsc::channel();
std::thread::spawn(move || -> color_eyre::Result<()> {
let hwnd = WindowsApi::create_border_window(PCWSTR(name.as_ptr()), h_module)?;
hwnd_sender.send(hwnd)?;
let mut message = MSG::default();
unsafe {
while GetMessageW(&mut message, HWND(hwnd), 0, 0).into() {
TranslateMessage(&message);
DispatchMessageW(&message);
std::thread::sleep(Duration::from_millis(10));
}
}
Ok(())
});
Ok(Self {
hwnd: hwnd_receiver.recv()?,
})
}
pub fn destroy(&self) -> color_eyre::Result<()> {
WindowsApi::destroy_window(self.hwnd())
}
pub fn update(&self, rect: &Rect) -> color_eyre::Result<()> {
// Make adjustments to the border
let mut rect = *rect;
rect.add_margin(BORDER_WIDTH.load(Ordering::SeqCst));
rect.add_padding(-BORDER_OFFSET.load(Ordering::SeqCst));
// Store the border rect so that it can be used by the callback
{
let mut rects = RECT_STATE.lock();
rects.insert(self.hwnd, rect);
}
// Update the position of the border
WindowsApi::set_border_pos(self.hwnd(), &rect, HWND((*Z_ORDER.lock()).into()))?;
// Invalidate the rect to trigger the callback to update colours etc.
self.invalidate();
Ok(())
}
pub fn invalidate(&self) {
let _ = unsafe { InvalidateRect(self.hwnd(), None, false) };
}
pub extern "system" fn callback(
window: HWND,
message: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
unsafe {
match message {
WM_PAINT => {
let rects = RECT_STATE.lock();
// With the rect that we stored in Self::update
if let Some(rect) = rects.get(&window.0).copied() {
// Grab the focus kind for this border
let focus_kind = {
FOCUSED_STATE
.lock()
.get(&window.0)
.copied()
.unwrap_or(FocusKind::Unfocused)
};
// Set up the brush to draw the border
let mut ps = PAINTSTRUCT::default();
let hdc = BeginPaint(window, &mut ps);
let hpen = CreatePen(
PS_SOLID | PS_INSIDEFRAME,
BORDER_WIDTH.load(Ordering::SeqCst),
COLORREF(match focus_kind {
FocusKind::Unfocused => UNFOCUSED.load(Ordering::SeqCst),
FocusKind::Single => FOCUSED.load(Ordering::SeqCst),
FocusKind::Stack => STACK.load(Ordering::SeqCst),
FocusKind::Monocle => MONOCLE.load(Ordering::SeqCst),
}),
);
let hbrush = WindowsApi::create_solid_brush(TRANSPARENCY);
// Draw the border
SelectObject(hdc, hpen);
SelectObject(hdc, hbrush);
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
EndPaint(window, &ps);
ValidateRect(window, None);
}
LRESULT(0)
}
WM_DESTROY => {
PostQuitMessage(0);
LRESULT(0)
}
_ => DefWindowProcW(window, message, wparam, lparam),
}
}
}
}

259
komoborders/src/main.rs Normal file
View File

@@ -0,0 +1,259 @@
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
#![allow(
clippy::missing_errors_doc,
clippy::redundant_pub_crate,
clippy::significant_drop_tightening,
clippy::significant_drop_in_scrutinee
)]
mod border;
use komorebi_client::Rect;
use komorebi_client::UnixListener;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::collections::HashMap;
use std::io::BufRead;
use std::io::BufReader;
use std::io::ErrorKind;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::time::Duration;
use uds_windows::UnixStream;
use windows::Win32::Foundation::HWND;
use crate::border::Border;
use crate::border::BORDER_WIDTH;
use crate::border::FOCUSED;
use crate::border::MONOCLE;
use crate::border::STACK;
use crate::border::UNFOCUSED;
use crate::border::Z_ORDER;
use komorebi::WindowsApi;
use komorebi_client::Rgb;
lazy_static! {
static ref BORDER_STATE: Mutex<HashMap<String, Border>> = Mutex::new(HashMap::new());
static ref RECT_STATE: Mutex<HashMap<isize, Rect>> = Mutex::new(HashMap::new());
static ref FOCUSED_STATE: Mutex<HashMap<isize, FocusKind>> = Mutex::new(HashMap::new());
}
#[derive(Copy, Clone)]
enum FocusKind {
Unfocused,
Single,
Stack,
Monocle,
}
pub fn read_commands_uds(stream: UnixStream) -> color_eyre::Result<()> {
let reader = BufReader::new(stream.try_clone()?);
for line in reader.lines() {
let message = komoborders_client::SocketMessage::from_str(&line?)?;
match message {
komoborders_client::SocketMessage::FocusedColour(r, g, b) => FOCUSED.store(
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
Ordering::SeqCst,
),
komoborders_client::SocketMessage::UnfocusedColour(r, g, b) => UNFOCUSED.store(
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
Ordering::SeqCst,
),
komoborders_client::SocketMessage::MonocleColour(r, g, b) => MONOCLE.store(
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
Ordering::SeqCst,
),
komoborders_client::SocketMessage::StackColour(r, g, b) => STACK.store(
komorebi::Colour::Rgb(Rgb::new(r, g, b)).into(),
Ordering::SeqCst,
),
komoborders_client::SocketMessage::Width(width) => {
BORDER_WIDTH.store(width, Ordering::SeqCst)
}
komoborders_client::SocketMessage::Offset(offset) => {
BORDER_WIDTH.store(offset, Ordering::SeqCst)
}
komoborders_client::SocketMessage::ZOrder(z_order) => {
let mut z = Z_ORDER.lock();
*z = z_order;
}
}
let borders = BORDER_STATE.lock();
for (_, border) in borders.iter() {
border.invalidate();
}
}
Ok(())
}
fn main() -> color_eyre::Result<()> {
WindowsApi::set_process_dpi_awareness_context()?;
let socket = dirs::data_local_dir()
.expect("there is no local data directory")
.join("komorebi")
.join("komoborders.sock");
match std::fs::remove_file(&socket) {
Ok(()) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
let listener = UnixListener::bind(&socket)?;
std::thread::spawn(move || {
for client in listener.incoming() {
match client {
Ok(stream) => match read_commands_uds(stream) {
Ok(()) => {
println!("processed message");
}
Err(error) => {
println!("{error}");
}
},
Err(error) => {
println!("{error}");
break;
}
}
}
});
let komorebi = komorebi_client::subscribe("komoborders")?;
for client in komorebi.incoming() {
match client {
Ok(subscription) => {
let reader = BufReader::new(subscription);
#[allow(clippy::lines_filter_map_ok)]
for line in reader.lines().flatten() {
if let Ok(notification) =
serde_json::from_str::<komorebi_client::Notification>(&line)
{
let mut borders = BORDER_STATE.lock();
// Check the state every time we receive a notification
let state = notification.state;
for m in state.monitors.elements() {
// Only operate on the focused workspace of each monitor
if let Some(ws) = m.focused_workspace() {
let mut should_proceed = true;
// Handle the monocle container separately
if let Some(monocle) = ws.monocle_container() {
for (_, border) in borders.iter() {
border.destroy()?;
}
borders.clear();
let border = borders
.entry(monocle.id().clone())
.or_insert_with(|| Border::create(monocle.id()).unwrap());
{
let mut focused = FOCUSED_STATE.lock();
focused.insert(border.hwnd, FocusKind::Monocle);
}
let rect = WindowsApi::window_rect(
monocle.focused_window().unwrap().hwnd(),
)?;
border.update(&rect)?;
should_proceed = false;
}
if should_proceed {
let is_maximized = WindowsApi::is_zoomed(HWND(
WindowsApi::foreground_window().unwrap_or_default(),
));
if is_maximized {
for (_, border) in borders.iter() {
border.destroy()?;
}
borders.clear();
should_proceed = false;
}
}
if should_proceed {
// Destroy any borders not associated with the focused workspace
let container_ids = ws
.containers()
.iter()
.map(|c| c.id().clone())
.collect::<Vec<_>>();
for (id, border) in borders.iter() {
if !container_ids.contains(id) {
border.destroy()?;
}
}
// Remove them from the border map
borders.retain(|k, _| container_ids.contains(k));
for (idx, c) in ws.containers().iter().enumerate() {
// Get the border entry for this container from the map or create one
let border = borders
.entry(c.id().clone())
.or_insert_with(|| Border::create(c.id()).unwrap());
// Update the focused state for all containers on this workspace
{
let mut focused = FOCUSED_STATE.lock();
focused.insert(
border.hwnd,
if idx != ws.focused_container_idx() {
FocusKind::Unfocused
} else {
if c.windows().len() > 1 {
FocusKind::Stack
} else {
FocusKind::Single
}
},
);
}
let rect = WindowsApi::window_rect(
c.focused_window().unwrap().hwnd(),
)?;
border.update(&rect)?;
}
}
}
}
}
}
}
Err(error) => {
if error.raw_os_error().expect("could not get raw os error") == 109 {
while komorebi_client::send_message(
&komorebi_client::SocketMessage::AddSubscriberSocket(String::from(
"komoborders",
)),
)
.is_err()
{
std::thread::sleep(Duration::from_secs(5));
}
}
}
}
}
Ok(())
}

View File

@@ -12,6 +12,7 @@ edition = "2021"
[dependencies]
komorebi-core = { path = "../komorebi-core" }
komoborders-client = { path = "../komoborders-client" }
bitflags = { version = "2", features = ["serde"] }
clap = { version = "4", features = ["derive"] }

View File

@@ -65,7 +65,7 @@ pub struct Rgb {
}
impl Rgb {
pub fn new(r: u32, g: u32, b: u32) -> Self {
pub const fn new(r: u32, g: u32, b: u32) -> Self {
Self { r, g, b }
}
}

View File

@@ -1255,12 +1255,21 @@ impl WindowManager {
WindowKind::Single => {
BORDER_COLOUR_SINGLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
BORDER_COLOUR_CURRENT.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
komoborders_client::send_message(
&komoborders_client::SocketMessage::FocusedColour(r, g, b),
)?;
}
WindowKind::Stack => {
BORDER_COLOUR_STACK.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
komoborders_client::send_message(
&komoborders_client::SocketMessage::StackColour(r, g, b),
)?;
}
WindowKind::Monocle => {
BORDER_COLOUR_MONOCLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
komoborders_client::send_message(
&komoborders_client::SocketMessage::MonocleColour(r, g, b),
)?;
}
}
@@ -1275,10 +1284,14 @@ impl WindowManager {
SocketMessage::BorderWidth(width) => {
BORDER_WIDTH.store(width, Ordering::SeqCst);
WindowsApi::invalidate_border_rect()?;
komoborders_client::send_message(&komoborders_client::SocketMessage::Width(width))?;
}
SocketMessage::BorderOffset(offset) => {
BORDER_OFFSET.store(offset, Ordering::SeqCst);
WindowsApi::invalidate_border_rect()?;
komoborders_client::send_message(&komoborders_client::SocketMessage::Offset(
offset,
))?;
}
SocketMessage::StackbarMode(mode) => {
let mut stackbar_mode = STACKBAR_MODE.lock();

View File

@@ -7,6 +7,7 @@ use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::workspace::Workspace;
use crate::Rgb;
use crate::ACTIVE_WINDOW_BORDER_STYLE;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_MONOCLE;
@@ -475,13 +476,36 @@ impl StaticConfig {
},
);
komoborders_client::send_message(&komoborders_client::SocketMessage::Width(
self.border_width.unwrap_or(8),
))?;
BORDER_OFFSET.store(self.border_offset.unwrap_or(-1), Ordering::SeqCst);
komoborders_client::send_message(&komoborders_client::SocketMessage::Width(
self.border_offset.unwrap_or(-1),
))?;
if let Some(colours) = &self.active_window_border_colours {
BORDER_COLOUR_SINGLE.store(u32::from(colours.single), Ordering::SeqCst);
BORDER_COLOUR_CURRENT.store(u32::from(colours.single), Ordering::SeqCst);
BORDER_COLOUR_STACK.store(u32::from(colours.stack), Ordering::SeqCst);
BORDER_COLOUR_MONOCLE.store(u32::from(colours.monocle), Ordering::SeqCst);
let single: Rgb = u32::from(colours.single).into();
komoborders_client::send_message(&komoborders_client::SocketMessage::FocusedColour(
single.r, single.g, single.b,
))?;
let stack: Rgb = u32::from(colours.stack).into();
komoborders_client::send_message(&komoborders_client::SocketMessage::StackColour(
stack.r, stack.g, stack.b,
))?;
let monocle: Rgb = u32::from(colours.monocle).into();
komoborders_client::send_message(&komoborders_client::SocketMessage::MonocleColour(
monocle.r, monocle.g, monocle.b,
))?;
}
let active_window_border_style = self.active_window_border_style.unwrap_or_default();

View File

@@ -124,6 +124,7 @@ use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
use windows::Win32::UI::WindowsAndMessaging::WM_CLOSE;
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
use windows::Win32::UI::WindowsAndMessaging::WS_DISABLED;
@@ -414,7 +415,8 @@ impl WindowsApi {
// top of other pop-up dialogs such as a file picker dialog from
// Firefox. When adjusting this in the future, it's important to check
// those dialog cases.
Self::set_window_pos(hwnd, layout, HWND_TOP, flags.bits())
// TODO: Make the HWND_X flag configurable
Self::set_window_pos(hwnd, layout, HWND_BOTTOM, flags.bits())
}
pub fn hide_border_window(hwnd: HWND) -> Result<()> {
@@ -424,6 +426,11 @@ impl WindowsApi {
Self::set_window_pos(hwnd, &Rect::default(), position, flags.bits())
}
pub fn set_border_pos(hwnd: HWND, layout: &Rect, position: HWND) -> Result<()> {
let flags = { SetWindowPosition::SHOW_WINDOW | SetWindowPosition::NO_ACTIVATE };
Self::set_window_pos(hwnd, layout, position, flags.bits())
}
/// set_window_pos calls SetWindowPos without any accounting for Window decorations.
fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
unsafe {
@@ -461,6 +468,13 @@ impl WindowsApi {
}
}
pub fn destroy_window(hwnd: HWND) -> Result<()> {
match Self::post_message(hwnd, WM_DESTROY, WPARAM(0), LPARAM(0)) {
Ok(()) => Ok(()),
Err(_) => Err(anyhow!("could not close window")),
}
}
pub fn hide_window(hwnd: HWND) {
Self::show_window(hwnd, SW_HIDE);
}