diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..aae2469 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pop-select" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +emacs = "0.11.0" +native-windows-gui = "1.0.12" +winapi = {version="0.3", features=["minwindef", "windef", "wingdi", "winuser", "libloaderapi", "processthreadsapi", "basetsd", "debugapi", "errhandlingapi"]} + +[lib] +crate-type = ["cdylib"] + +[build-dependencies] +embed-resource = "1" diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..dc8b135 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,84 @@ +* 功能介绍 +本工具是为Windows Emacs用户定制的小工具集,目前主要实现的功能都跟窗口有关,含: +1. `pop-select/gui-set-transparent` +设置Emacs窗口透明,设置代码: +``` +(ignore-errors (module-load "pop-select.dll全路径,如果没有加入bin路径的话")) +(when (functionp 'pop-select/gui-set-transparent) + (defvar cur-transparent 255) + (defconst step-transparent 20) + (pop-select/gui-set-transparent cur-transparent) + (defun dec-transparent() + (interactive) + (setq cur-transparent (min 255 (+ cur-transparent step-transparent))) + (pop-select/gui-set-transparent cur-transparent)) + (defun inc-transparent() + (interactive) + (setq cur-transparent (max 0 (- cur-transparent step-transparent))) + (pop-select/gui-set-transparent cur-transparent)) + (global-set-key (kbd "") 'dec-transparent) + (global-set-key (kbd "") 'inc-transparent) + ) +``` +2. `pop-select/pop-select` +弹出一个竖型列表窗口,然后可以按ctrl+tab切换到下一项,ctrl+tab+shift切换到上一项,释放按键后返回所选项给emacs +``` +(when (fboundp 'pop-select/pop-select) + (defun my-pop-select(&optional backward) + (interactive) + (let* ((myswitch-buffer-list (copy-sequence (ep-tabbar-buffer-list) + ) + ) (vec_name []) + sel + ) + (cl-dolist (buf myswitch-buffer-list) + (setq vec_name (vconcat vec_name (list (buffer-name buf))))) + ;; 返回序号 + (setq sel (pop-select/pop-select vec_name (if backward + (1- (length vec_name)) + 1 + ))) + (let ((buf (switch-to-buffer (nth sel myswitch-buffer-list)))) + (when (and (bufferp buf) (featurep 'wcy-desktop)) + (with-current-buffer buf + (when (eq major-mode 'not-loaded-yet) + (wcy-desktop-load-file)))) + ) + ) + ) + (global-set-key (kbd "") 'my-pop-select) + (global-set-key (if (string-equal system-type "windows-nt") + (kbd "") + (kbd "")) + (lambda ()(interactive) + (my-pop-select t))) + ) +``` +3. `pop-select/beacon-blink`和`pop-select/beacon-set-parameters` +用于替换beacon的闪烁效果,完全不卡Emacs窗口,因为是另起了一个专门的ui线程来画beacon +``` +(when (fboundp 'pop-select/beacon-set-parameters) + ;; 51afef + (pop-select/beacon-set-parameters 300 20 #x51 #xaf #xef 50) + (use-package beacon + :defer 1.5 + :init + (setq beacon-blink-when-focused t) + (setq beacon-blink-delay 0.01) + (setq beacon-blink-duration 0.2) + (setq beacon-blink-when-window-scrolls nil) ; 开启了auto save,保存时都会闪故而屏蔽 + :config + (beacon-mode 1) + (defadvice beacon-blink (around my-beacon-blink activate) + ;; 目前偶尔不是emacs时也弹窗 + ;; (message (concat (symbol-name this-command) " " (symbol-name last-command))) + (when (frame-visible-p (window-frame)) ;; 可以阻止最小化时弹窗 + (let ((p (window-absolute-pixel-position))) + (when p + (pop-select/beacon-blink (car p) ; x + (cdr p) ; y + (truncate (* beacon-blink-duration 1000)) ; timer + (truncate (* beacon-blink-delay 1000)) ; delay + )))))) + ) +``` diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..3092f97 --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +锘縠xtern crate embed_resource; + +fn main() { + embed_resource::compile("src/res.rc"); +} diff --git a/src/beacon.rs b/src/beacon.rs new file mode 100644 index 0000000..fb8873b --- /dev/null +++ b/src/beacon.rs @@ -0,0 +1,288 @@ +锘縰se emacs::{defun, Result}; +use winapi::ctypes::*; +use winapi::shared::basetsd::*; +use winapi::shared::minwindef::*; +use winapi::shared::windef::*; +#[cfg(debug_assertions)] +use winapi::um::debugapi::*; +#[cfg(debug_assertions)] +use winapi::um::errhandlingapi::*; +use winapi::um::libloaderapi::*; +use winapi::um::processthreadsapi::*; +use winapi::um::wingdi::*; +use winapi::um::winuser::*; + +static mut BEACON_WND: HWND = std::ptr::null_mut(); +static mut BEACON_TID: DWORD = 0; +static mut BEACON_WIDTH: DWORD = 300; +static mut BEACON_HEIGHT: DWORD = 20; +static mut BEACON_R: u8 = 255; +static mut BEACON_G: u8 = 0; +static mut BEACON_B: u8 = 0; +static mut BEACON_X: usize = 0; +static mut BEACON_Y: usize = 0; +static mut BEACON_DURATION: usize = 0; +static mut BEACON_DURATION_COUNT: isize = 0; // 鐧惧垎姣 +static mut TIMER_DURATION_EACH: usize = 100; +const WM_SHOW_BEACON: UINT = WM_USER + 0x0001; +const WM_BEACON_SET_SIZE: UINT = WM_USER + 0x0002; +const TIMER_DURATION: UINT_PTR = 1; +const TIMER_DELAY: UINT_PTR = 2; + +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; + +fn to_wstring(s: &str) -> Vec { + OsStr::new(s) + .encode_wide() + .chain(std::iter::once(0)) + .collect() +} + +pub unsafe extern "system" fn window_proc( + hwnd: HWND, + msg: UINT, + wparam: WPARAM, + lparam: LPARAM, +) -> LRESULT { + if msg == WM_DESTROY { + // PostQuitMessage(0); + return 0; + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} +fn on_timer(left: isize) { + unsafe { + let left = (left as f64 / (BEACON_DURATION as f64 / TIMER_DURATION_EACH as f64) + * BEACON_WIDTH as f64) as i32; + #[cfg(debug_assertions)] + { + let right = BEACON_WIDTH as i32 - left; + let msg = format!("left:{}, right:{}", left, right); + OutputDebugStringW(to_wstring(&msg).as_slice().as_ptr()); + } + + let mut vertex: [TRIVERTEX; 2] = std::mem::zeroed(); + vertex[0].x = 0; + vertex[0].y = 0; + vertex[0].Red = (BEACON_R as u16) << 8; + vertex[0].Green = (BEACON_G as u16) << 8; + vertex[0].Blue = (BEACON_B as u16) << 8; + vertex[0].Alpha = 0x0000; + + vertex[1].x = left as i32; + vertex[1].y = BEACON_HEIGHT as i32; + vertex[1].Red = 0; + vertex[1].Green = 0; + vertex[1].Blue = 0; + vertex[1].Alpha = 0x0000; + + // Create a GRADIENT_RECT structure that + // references the TRIVERTEX vertices. + let mut g_rect: GRADIENT_RECT = std::mem::zeroed(); + g_rect.UpperLeft = 0; + g_rect.LowerRight = 1; + let hdc = GetDC(BEACON_WND); + if !hdc.is_null() { + GradientFill( + hdc, + &mut vertex as *mut _, + 2, + &mut g_rect as *mut _ as *mut _, + 1, + GRADIENT_FILL_RECT_H, + ); + let bb = GetStockObject(BLACK_BRUSH as i32); + let mut rc: RECT = std::mem::zeroed(); + rc.left = left; + rc.top = 0; + rc.right = BEACON_WIDTH as i32; + rc.bottom = BEACON_HEIGHT as i32; + FillRect(hdc, &rc, bb as *mut _); + ReleaseDC(BEACON_WND, hdc); + } + } +} +fn show_wnd() { + unsafe { + if BEACON_WND.is_null() { + return; + } + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"WM_SHOW_BEACON!\0" as *const _ as *const _); + } + ShowWindow(BEACON_WND, SW_SHOW); + + BEACON_DURATION_COUNT = (BEACON_DURATION as f64 / TIMER_DURATION_EACH as f64) as isize; + SetTimer(BEACON_WND, TIMER_DURATION, TIMER_DURATION_EACH as u32, None); + // SetForegroundWindow(BEACON_WND); // 杩欎釜浼氬彂鐢熺劍鐐瑰垏鎹 + SetWindowPos( + BEACON_WND, + HWND_TOPMOST, + BEACON_X as c_int, + BEACON_Y as c_int, + BEACON_WIDTH as c_int, + BEACON_HEIGHT as c_int, + SWP_SHOWWINDOW, + ); + on_timer(BEACON_DURATION_COUNT); + } +} + +fn create_wnd() { + unsafe { + if !BEACON_WND.is_null() { + return; + } + let wc = WNDCLASSEXW { + cbSize: std::mem::size_of::() as UINT, + style: CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, + lpfnWndProc: Some(window_proc), + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: GetModuleHandleW(std::ptr::null_mut()) as HINSTANCE, + hIcon: std::ptr::null_mut(), + hCursor: LoadCursorW(std::ptr::null_mut(), IDC_ARROW), + hbrBackground: GetStockObject(WHITE_BRUSH as i32) as HBRUSH, + lpszMenuName: std::ptr::null_mut(), + lpszClassName: to_wstring("rust_window_class").as_ptr(), + hIconSm: std::ptr::null_mut(), + }; + if RegisterClassExW(&wc) == 0 { + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"RegisterClassEx failed\0".as_ptr() as *const _); + } + } + + // 閫忔槑瑕佸甫WS_EX_LAYERED + let hwnd = CreateWindowExW( + WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_LAYERED, // 浠诲姟鏍忔棤鏍囬锛屾樉绀烘椂鏃犵劍鐐瑰垏鎹 + wc.lpszClassName, + to_wstring("Rust Window").as_ptr(), + WS_POPUP, // 璋冭瘯鐢▅ WS_BORDER + 0, + 0, + BEACON_WIDTH as i32, + BEACON_HEIGHT as i32, + std::ptr::null_mut(), + std::ptr::null_mut(), + wc.hInstance, + std::ptr::null_mut(), + ); + if hwnd == std::ptr::null_mut() { + #[cfg(debug_assertions)] + { + let err = GetLastError(); + let msg = format!("err:{}", err); + OutputDebugStringW(to_wstring(&msg).as_slice().as_ptr()); + } + return; + } + // 鎸囧畾閫忔槑鑹 + SetLayeredWindowAttributes( + hwnd, + RGB(0, 0, 0), + 200, // LWA_COLORKEY杩欎釜灏辨病鏁堟灉锛屾槸鍏ㄩ忔槑鐨 + LWA_COLORKEY // + // LWA_ALPHA, + ); + BEACON_WND = hwnd; + } +} + +pub fn becaon_init() { + std::thread::spawn(move || unsafe { + BEACON_TID = GetCurrentThreadId(); + let mut msg = MSG { + hwnd: std::ptr::null_mut(), + message: 0, + wParam: 0, + lParam: 0, + time: 0, + pt: POINT { x: 0, y: 0 }, + }; + loop { + let res = GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0); + if res == 0 || res == -1 { + break; + } + TranslateMessage(&msg); + // 娑堟伅涓嶈兘鍘粀indow_proc閲屽鐞 + if msg.message == WM_SHOW_BEACON { + create_wnd(); + if !BEACON_WND.is_null() { + KillTimer(BEACON_WND, TIMER_DURATION); + SetTimer(BEACON_WND, TIMER_DELAY, msg.wParam as u32, None); + } else { + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"window is null!\0".as_ptr() as *const _); + } + } + } else if msg.message == WM_BEACON_SET_SIZE { + BEACON_WIDTH = msg.wParam as DWORD; + BEACON_HEIGHT = msg.lParam as DWORD; + if !BEACON_WND.is_null() { + DestroyWindow(BEACON_WND); + BEACON_WND = std::ptr::null_mut(); + } + } else if msg.message == WM_TIMER { + if msg.wParam == TIMER_DURATION { + BEACON_DURATION_COUNT = BEACON_DURATION_COUNT - 1; + if BEACON_DURATION_COUNT < 0 { + #[cfg(debug_assertions)] + { + OutputDebugStringA(b"kill timer!\0" as *const _ as *const _); + } + ShowWindow(BEACON_WND, SW_HIDE); + KillTimer(BEACON_WND, TIMER_DURATION); + } else { + on_timer(BEACON_DURATION_COUNT); + } + } else if msg.wParam == TIMER_DELAY { + KillTimer(BEACON_WND, TIMER_DELAY); + show_wnd(); + } + } + DispatchMessageW(&msg); + } + }); +} + +#[defun] +fn set_parameters( + width: usize, + height: usize, + r: u8, + g: u8, + b: u8, + duration_step: usize, +) -> Result<()> { + unsafe { + PostThreadMessageW( + BEACON_TID, + WM_BEACON_SET_SIZE, + width as WPARAM, + height as LPARAM, + ); + BEACON_R = r; + BEACON_G = g; + BEACON_B = b; + TIMER_DURATION_EACH = duration_step; + } + Ok(()) +} + +#[defun] +fn blink(x: usize, y: usize, timer: usize, delay: usize) -> Result<()> { + unsafe { + BEACON_X = x; + BEACON_Y = y; + BEACON_DURATION = timer; + PostThreadMessageW(BEACON_TID, WM_SHOW_BEACON, delay as WPARAM, 0 as LPARAM); + } + Ok(()) +} diff --git a/src/gui.rs b/src/gui.rs new file mode 100644 index 0000000..5c71e00 --- /dev/null +++ b/src/gui.rs @@ -0,0 +1,250 @@ +锘縰se emacs::{defun, Result}; +extern crate native_windows_gui as nwg; +use winapi::um::wingdi::RGB; +use std::rc::Rc; +use winapi::shared::basetsd::LONG_PTR; +use winapi::shared::minwindef::*; +use winapi::shared::windef::*; +use winapi::um::processthreadsapi::*; +use winapi::um::winuser::GetForegroundWindow; +use winapi::um::winuser::*; + +// hotkey鍙傝 https://blog.csdn.net/x356982611/article/details/16341797 +static mut HACCEL: usize = 0; + +pub fn popup( + h_mod: winapi::shared::minwindef::HINSTANCE, + pop_list: Vec, + to_sel: usize, +) -> Option { + if unsafe { HACCEL == 0 } { + unsafe { + HACCEL = + winapi::um::winuser::LoadAcceleratorsA(h_mod, b"IDR_ACCELERATOR1\0".as_ptr() as _) + as usize; + } + + nwg::init().ok()?; + nwg::Font::set_global_family("Segoe UI").ok()?; + } + + let mut window = Default::default(); + let mut list = Default::default(); + let layout = Default::default(); + const LIST_ITEM_HEIGHT: u16 = 23; + nwg::Window::builder() + .size(( + 300, + std::cmp::min( + nwg::Monitor::height(), + LIST_ITEM_HEIGHT as i32 * (pop_list.len() + 1) as i32, // 闇瑕侀鐣欎竴琛岋紝涓嶇劧浼氭湁婊氬姩鏉 + ), + )) + .ex_flags(WS_EX_TOOLWINDOW) // 鏃犱换鍔℃爮绐楀彛 + .flags(nwg::WindowFlags::POPUP | nwg::WindowFlags::VISIBLE) + .position((300, 300)) + // .title("Basic example") + .topmost(true) + .center(true) + .build(&mut window) + .ok()?; + + nwg::ListBox::builder() + .collection(pop_list) + .flags(nwg::ListBoxFlags::VISIBLE | nwg::ListBoxFlags::TAB_STOP) + // .focus(true) // 涓嶈兘璁剧疆focus锛屽惁鍒欐姄涓嶅埌蹇嵎閿 + .selected_index(Some(0)) + .parent(&window) + .build(&mut list) + .ok()?; + + nwg::GridLayout::builder() + .parent(&window) + .margin([1, 1, 1, 1]) + .spacing(0) + .child(0, 0, &list) + // .child_item(nwg::GridLayoutItem::new(&hello_button, 0, 1, 1, 2)) + .build(&layout) + .ok()?; + + let window = Rc::new(window); + let list = Rc::new(list); + let list1 = list.clone(); + list.set_selection(Some(to_sel)); + unsafe { + SendMessageW( + list.handle.hwnd().unwrap(), + LB_SETITEMHEIGHT, + 0, + MAKELONG(LIST_ITEM_HEIGHT, 0) as isize, + ); + } + + // 鎬绘槸閬囧埌寮瑰嚭鐨勭獥鍙eけ鍘荤劍鐐圭殑鎯呭喌锛屽弬鑰僃indwindow閭f牱鍘诲仛 + unsafe { + let hwnd = window.handle.hwnd().unwrap(); + let h_fore_wnd = GetForegroundWindow(); + let dw_cur_id = GetCurrentThreadId(); + let dw_fore_id = GetWindowThreadProcessId(h_fore_wnd, std::ptr::null_mut()); + + AttachThreadInput(dw_cur_id, dw_fore_id, TRUE); + ShowWindow(hwnd, SW_SHOWNORMAL); + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + SetForegroundWindow(hwnd); + SetFocus(hwnd); + AttachThreadInput(dw_cur_id, dw_fore_id, FALSE); + } + // hook list涔熷彲浠ワ紝浣嗘槸澶存ctrl+shift+tab娌″弽搴 + let handler = nwg::bind_raw_event_handler(&window.handle, 0x10000, move |_hwnd, msg, w, _l| { + if msg == WM_COMMAND { + let cmd = winapi::shared::minwindef::LOWORD(w as u32); + const ID_CTRLTAB: u16 = 40001; + const ID_CTRLTAB_SHIFT: u16 = 40002; + const ID_CTRL_P: u16 = 40003; + const ID_CTRL_N: u16 = 40004; + let mut to_next = false; + let mut to_prev = false; + if cmd == ID_CTRLTAB || cmd == ID_CTRL_N { + to_next = true; + } else if ID_CTRLTAB_SHIFT == cmd || ID_CTRL_P == cmd { + to_prev = true; + } + if to_next || to_prev { + let to_sel; + let len = list1.len(); + let cur_sel = list1.selection(); + if len != 0 { + if to_prev { + if let Some(cur) = cur_sel { + let mut cur = cur as isize; + cur -= 1; + if cur < 0 { + cur = len as isize - 1; + } + to_sel = Some(cur as usize); + } else { + to_sel = Some(len - 1); + } + } else { + if let Some(mut cur) = cur_sel { + cur += 1; + if cur >= len { + cur = 0; + } + to_sel = Some(cur); + } else { + to_sel = Some(0); + } + } + list1.set_selection(to_sel); + } + } + } else if msg == WM_KEYUP { + let ctrl = unsafe { GetAsyncKeyState(VK_CONTROL) as i32 & 0x8000 }; + if ctrl == 0 { + nwg::stop_thread_dispatch(); + } + } else if msg == WM_KILLFOCUS { + nwg::stop_thread_dispatch(); + } + None + }) + .ok()?; + + // 鎷疯礉nwg::dispatch_thread_events()浠g爜锛屾坊鍔燭ranslateAcceleratorW + unsafe { + let mut msg: MSG = std::mem::zeroed(); + while GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) != 0 { + if TranslateAcceleratorW(msg.hwnd, HACCEL as _, &mut msg) != 0 { + continue; + } + if IsDialogMessageW(GetAncestor(msg.hwnd, GA_ROOT), &mut msg) == 0 { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + } + + nwg::unbind_raw_event_handler(&handler).ok(); + // dbg!(&selection_string); + // *selection_string + list.selection() +} + +pub fn gui_init() { + // 涓嶇煡閬撴湁娌℃湁鏁堟灉锛屼繚鐣欏惂 + unsafe { + let mut dwtimeout: DWORD = u32::MAX as DWORD; + SystemParametersInfoA( + SPI_GETFOREGROUNDLOCKTIMEOUT, + 0, + &mut dwtimeout as *mut _ as *mut _, + 0, + ); + if dwtimeout >= 100 { + SystemParametersInfoA( + SPI_SETFOREGROUNDLOCKTIMEOUT, + 0, + std::ptr::null_mut(), + SPIF_SENDCHANGE | SPIF_UPDATEINIFILE, + ); + } + } +} + +struct EnumData { + pid: DWORD, + ret: HWND, +} +fn enum_windows_item(data: &mut EnumData, hwnd: HWND) -> BOOL { + let mut p: DWORD = 0; + if unsafe { GetWindowThreadProcessId(hwnd, &mut p as *mut _) != 0 } && p == data.pid { + unsafe { + let mut buf: [u8; MAX_PATH] = std::mem::zeroed(); + let len = GetClassNameA(hwnd, &mut buf as *mut _ as *mut _, MAX_PATH as i32); + if len == 5 && &buf[0..5] == b"Emacs"{ + data.ret = hwnd; + return 0; + } + } + } + 1 +} +unsafe extern "system" fn enum_callback(win_hwnd: HWND, arg: LONG_PTR) -> BOOL { + let pthis = arg as *mut EnumData; + enum_windows_item(&mut *pthis, win_hwnd) +} + +fn get_current_process_wnd() -> Option { + let mut data = EnumData { + pid: unsafe { winapi::um::processthreadsapi::GetCurrentProcessId() }, + ret: std::ptr::null_mut(), + }; + unsafe { + EnumWindows(Some(enum_callback), &mut data as *mut _ as LONG_PTR); + } + if !data.ret.is_null() { + Some(data.ret) + } else { + None + } +} + + +#[defun] +pub fn set_transparent(alpha: u8) -> Result<()> { + if let Some(hwnd) = get_current_process_wnd(){ + unsafe{ + SetWindowLongW(hwnd , GWL_EXSTYLE, (WS_EX_LAYERED).try_into().unwrap()); + if 0 != SetLayeredWindowAttributes( + hwnd, + RGB(0, 0, 0), + alpha, + LWA_ALPHA, + ){ + } + } + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5a22407 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,57 @@ +锘縨od gui; +mod beacon; +// (module-load "pop_select") +// (message (pop-select/select (vector "aa" "bb" "aa" "bb""aa" "bb""aa" "bb""aa" "bb""aa" "bb""aa" "涓枃"))) +// (global-set-key (kbd "") (lambda () (interactive)(pop-select/select (vector "aa" "bb" "aa" "bb""aa" "bb""aa" "bb""aa" "bb""aa" "bb""aa" "涓枃")))) + +// 闇瑕佽缃幆澧冨彉閲廘IBCLANG_PATH E:\Program Files\LLVM\bin\ +use emacs::{ + defun, + Env, + Result, + Vector, // Value +}; + +// Emacs won't load the module without this. +emacs::plugin_is_GPL_compatible!(); + +#[emacs::module(separator = "/")] +fn init(_: &Env) -> Result<()> { + gui::gui_init(); + beacon::becaon_init(); + Ok(()) +} + +#[defun] +fn pop_select(name: Vector, to_sel: usize) -> Result { + let mut v = Vec::new(); + if let Ok(s) = name.size() { + for i in 0..s { + if let Ok(ss) = name.get(i) { + v.push(ss); + } + } + if let Some(s) = gui::popup(unsafe { DLL_MOD }, v, to_sel) { + return Ok(s); + } + } + return Ok(0); +} +static mut DLL_MOD: winapi::shared::minwindef::HINSTANCE = std::ptr::null_mut(); + +#[no_mangle] +#[allow(non_snake_case)] +extern "system" fn DllMain( + hinstAcc: winapi::shared::minwindef::HINSTANCE, + reason: u32, + _: winapi::shared::minwindef::LPVOID, +) -> i32 { + match reason { + 1 => unsafe { + DLL_MOD = hinstAcc; + }, + 0 => (), + _ => (), + } + 1 +} diff --git a/src/res.rc b/src/res.rc new file mode 100644 index 0000000..a7275ce Binary files /dev/null and b/src/res.rc differ diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 0000000..47a7b37 Binary files /dev/null and b/src/resource.h differ