#![allow(clippy::many_single_char_names)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::type_complexity)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused_variables)]
use crate::{
bindings::root, register_plugin_destroy_hook, PluginContext, Swell,
SwellFunctionPointers,
};
static mut INSTANCE: Option<Swell> = None;
impl Swell {
pub fn make_available_globally(functions: Swell) {
static INIT_INSTANCE: std::sync::Once = std::sync::Once::new();
unsafe {
INIT_INSTANCE.call_once(|| {
INSTANCE = Some(functions);
register_plugin_destroy_hook(|| INSTANCE = None);
});
}
}
pub fn get() -> &'static Swell {
unsafe {
INSTANCE.as_ref().expect(
"call `make_available_globally()` before using `get()`",
)
}
}
pub fn pointers(&self) -> &SwellFunctionPointers {
&self.pointers
}
pub fn plugin_context(&self) -> &PluginContext {
self.plugin_context
.as_ref()
.expect("plug-in context not available on demo instances")
}
pub unsafe fn CreateDialogParam(
&self,
hinst: root::HINSTANCE,
resid: *const ::std::os::raw::c_char,
par: root::HWND,
dlgproc: root::DLGPROC,
param: root::LPARAM,
) -> root::HWND {
#[cfg(target_family = "unix")]
{
self.SWELL_CreateDialog(
root::SWELL_curmodule_dialogresource_head,
resid,
par,
dlgproc,
param,
)
}
#[cfg(target_family = "windows")]
#[allow(clippy::cast_ptr_alignment)]
{
winapi::um::winuser::CreateDialogParamW(
hinst as _,
resid as _,
par as _,
std::mem::transmute(dlgproc),
param,
) as _
}
}
pub unsafe fn LoadMenu(
&self,
hinst: root::HINSTANCE,
resid: *const ::std::os::raw::c_char,
) -> root::HMENU {
#[cfg(target_family = "unix")]
{
self.SWELL_LoadMenu(root::SWELL_curmodule_menuresource_head, resid)
}
#[cfg(target_family = "windows")]
#[allow(clippy::cast_ptr_alignment)]
{
winapi::um::winuser::LoadMenuW(hinst as _, resid as _) as _
}
}
pub unsafe fn FillRect(
&self,
ctx: root::HDC,
r: *const root::RECT,
br: root::HBRUSH,
) {
#[cfg(target_family = "unix")]
{
self.SWELL_FillRect(ctx, r, br);
}
#[cfg(target_family = "windows")]
#[allow(clippy::cast_ptr_alignment)]
{
winapi::um::winuser::FillRect(ctx as _, r as _, br as _);
}
}
pub unsafe fn DrawText(
&self,
ctx: root::HDC,
buf: *const ::std::os::raw::c_char,
len: ::std::os::raw::c_int,
r: *mut root::RECT,
align: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int {
#[cfg(target_family = "unix")]
{
self.SWELL_DrawText(ctx, buf, len, r, align)
}
#[cfg(target_family = "windows")]
#[allow(clippy::cast_ptr_alignment)]
{
let utf16_string = utf8_to_16(buf);
let result = winapi::um::winuser::DrawTextW(
ctx as _,
utf16_string.as_ptr(),
len,
r as _,
align as _,
);
std::mem::drop(utf16_string);
result
}
}
pub unsafe fn SetWindowText(
&self,
hwnd: root::HWND,
text: *const ::std::os::raw::c_char,
) -> root::BOOL {
#[cfg(target_family = "unix")]
{
self.SetDlgItemText(hwnd, 0, text)
}
#[cfg(target_family = "windows")]
{
let utf16_string = utf8_to_16(text);
let result = winapi::um::winuser::SetWindowTextW(
hwnd as _,
utf16_string.as_ptr(),
);
std::mem::drop(utf16_string);
result as _
}
}
pub unsafe fn GetWindowText(
&self,
hwnd: root::HWND,
lpString: root::LPSTR,
nMaxCount: std::os::raw::c_int,
) -> root::BOOL {
#[cfg(target_family = "unix")]
{
self.GetDlgItemText(hwnd, 0, lpString, nMaxCount)
}
#[cfg(target_family = "windows")]
{
let len =
with_utf16_to_8(lpString, nMaxCount, |buffer, max_size| {
winapi::um::winuser::GetWindowTextW(
hwnd as _, buffer, max_size,
) as _
});
if len == 0 {
0
} else {
1
}
}
}
pub fn RGB(r: u8, g: u8, b: u8) -> root::DWORD {
#[cfg(target_family = "unix")]
{
((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}
#[cfg(target_family = "windows")]
{
(r as u32) | ((g as u32) << 8) | ((b as u32) << 16)
}
}
pub fn GetRValue(color: root::DWORD) -> u8 {
#[cfg(target_family = "unix")]
{
((color >> 16) & 0xff) as _
}
#[cfg(target_family = "windows")]
{
(color & 0xff) as _
}
}
pub fn GetGValue(color: root::DWORD) -> u8 {
((color >> 8) & 0xff) as _
}
pub fn GetBValue(color: root::DWORD) -> u8 {
#[cfg(target_family = "unix")]
{
(color & 0xff) as _
}
#[cfg(target_family = "windows")]
{
((color >> 16) & 0xff) as _
}
}
}
#[cfg(target_family = "windows")]
impl Swell {
pub unsafe fn UpdateWindow(&self, hwnd: root::HWND) {
winapi::um::winuser::UpdateWindow(hwnd as _);
}
pub unsafe fn SendMessage(
&self,
hwnd: root::HWND,
msg: root::UINT,
wParam: root::WPARAM,
lParam: root::LPARAM,
) -> root::LRESULT {
if lParam != 0 && lparam_is_string(msg) {
let utf16_string = utf8_to_16(lParam as _);
let result = winapi::um::winuser::SendMessageW(
hwnd as _,
msg,
wParam,
utf16_string.as_ptr() as _,
);
std::mem::drop(utf16_string);
result
} else {
winapi::um::winuser::SendMessageW(hwnd as _, msg, wParam, lParam)
}
}
pub unsafe fn PostMessage(
&self,
hwnd: root::HWND,
msg: root::UINT,
wParam: root::WPARAM,
lParam: root::LPARAM,
) -> root::BOOL {
if lParam != 0 && lparam_is_string(msg) {
let utf16_string = utf8_to_16(lParam as _);
let result = winapi::um::winuser::PostMessageW(
hwnd as _,
msg,
wParam,
utf16_string.as_ptr() as _,
);
std::mem::drop(utf16_string);
result as _
} else {
winapi::um::winuser::PostMessageW(hwnd as _, msg, wParam, lParam)
as _
}
}
pub unsafe fn MessageBox(
&self,
hwndParent: root::HWND,
text: *const ::std::os::raw::c_char,
caption: *const ::std::os::raw::c_char,
type_: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int {
let text_utf16 = utf8_to_16(text);
let caption_utf16 = utf8_to_16(caption);
let result = winapi::um::winuser::MessageBoxW(
hwndParent as _,
text_utf16.as_ptr() as _,
caption_utf16.as_ptr() as _,
type_ as _,
);
std::mem::drop(text_utf16);
result as _
}
pub unsafe fn SetMenuItemInfo(
&self,
hMenu: root::HMENU,
pos: ::std::os::raw::c_int,
byPos: root::BOOL,
mi: *mut root::MENUITEMINFO,
) -> root::BOOL {
let mi = *mi;
let mut utf16_mi = utf8_to_16_menu_item_info(&mi);
if menu_item_needs_string_conversion(mi) {
let mut utf16_string = utf8_to_16(mi.dwTypeData);
utf16_mi.dwTypeData = utf16_string.as_mut_ptr();
let result = winapi::um::winuser::SetMenuItemInfoW(
hMenu as _,
pos as _,
byPos as _,
&utf16_mi as *const _,
);
std::mem::drop(utf16_string);
result as _
} else {
let result = winapi::um::winuser::SetMenuItemInfoW(
hMenu as _,
pos as _,
byPos as _,
&utf16_mi as *const _,
);
result as _
}
}
pub unsafe fn InsertMenuItem(
&self,
hMenu: root::HMENU,
pos: ::std::os::raw::c_int,
byPos: root::BOOL,
mi: *mut root::MENUITEMINFO,
) {
let mi = *mi;
let mut utf16_mi = utf8_to_16_menu_item_info(&mi);
if menu_item_needs_string_conversion(mi) {
let mut utf16_string = utf8_to_16(mi.dwTypeData);
utf16_mi.dwTypeData = utf16_string.as_mut_ptr();
let result = winapi::um::winuser::InsertMenuItemW(
hMenu as _,
pos as _,
byPos as _,
&utf16_mi as *const _,
);
std::mem::drop(utf16_string);
} else {
let result = winapi::um::winuser::InsertMenuItemW(
hMenu as _,
pos as _,
byPos as _,
&utf16_mi as *const _,
);
}
}
pub unsafe fn GetMenuItemInfo(
&self,
hMenu: root::HMENU,
pos: ::std::os::raw::c_int,
byPos: root::BOOL,
mi: *mut root::MENUITEMINFO,
) -> root::BOOL {
let mut mi = *mi;
if !mi.dwTypeData.is_null() {
todo!("Getting string information from menu item is not yet implemented.")
}
let mut utf16_mi = utf8_to_16_menu_item_info(&mi);
let result = winapi::um::winuser::GetMenuItemInfoW(
hMenu as _,
pos as _,
byPos as _,
&mut utf16_mi as _,
);
mi.cbSize = utf16_mi.cbSize;
mi.fMask = utf16_mi.fMask;
mi.fType = utf16_mi.fType;
mi.fState = utf16_mi.fState;
mi.wID = utf16_mi.wID;
mi.hSubMenu = utf16_mi.hSubMenu as _;
mi.hbmpChecked = utf16_mi.hbmpChecked as _;
mi.hbmpUnchecked = utf16_mi.hbmpUnchecked as _;
mi.dwItemData = utf16_mi.dwItemData;
mi.dwTypeData = std::ptr::null_mut();
mi.cch = utf16_mi.cch as _;
mi.hbmpItem = utf16_mi.hbmpItem as _;
result as _
}
pub fn CF_TEXT(&self) -> root::UINT {
#[cfg(target_family = "unix")]
{
unsafe {
self.RegisterClipboardFormat(
c_str_macro::c_str!("SWELL__CF_TEXT").as_ptr(),
)
}
}
#[cfg(target_family = "windows")]
1
}
}
impl std::fmt::Debug for SwellFunctionPointers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SwellFunctionPointers")
.field("loaded_count", &self.loaded_count)
.field("total_count", &Self::TOTAL_COUNT)
.finish()
}
}
#[cfg(target_family = "windows")]
pub(crate) unsafe fn utf8_to_16(
raw_utf8: *const std::os::raw::c_char,
) -> Vec<u16> {
use std::ffi::{CStr, OsStr};
use std::iter::once;
let utf8_c_str = CStr::from_ptr(raw_utf8);
let str = utf8_c_str.to_string_lossy();
use std::os::windows::ffi::OsStrExt;
OsStr::new(str.as_ref())
.encode_wide()
.chain(once(0))
.collect()
}
#[cfg(target_family = "windows")]
pub(crate) unsafe fn with_utf16_to_8(
utf8_target_buffer: *mut std::os::raw::c_char,
requested_max_size: std::os::raw::c_int,
fill_utf16_source_buffer: impl FnOnce(*mut u16, std::os::raw::c_int) -> usize,
) -> usize {
let mut utf16_vec: Vec<u16> =
Vec::with_capacity(requested_max_size as usize);
let len =
fill_utf16_source_buffer(utf16_vec.as_mut_ptr(), requested_max_size);
if len == 0 {
return 0;
}
utf16_vec.set_len(len);
let string = String::from_utf16_lossy(&utf16_vec);
let c_string = match std::ffi::CString::new(string) {
Ok(s) => s,
Err(_) => {
return 0;
}
};
let source_bytes = c_string.as_bytes_with_nul();
let target_bytes = std::slice::from_raw_parts_mut(
utf8_target_buffer,
requested_max_size as usize,
);
let source_bytes_signed = &*(source_bytes as *const [u8] as *const [i8]);
target_bytes[..source_bytes.len()].copy_from_slice(source_bytes_signed);
len
}
#[cfg(target_family = "windows")]
fn lparam_is_string(msg: root::UINT) -> bool {
use crate::raw;
matches!(msg, raw::CB_INSERTSTRING | raw::CB_ADDSTRING)
}
#[cfg(target_family = "windows")]
fn utf8_to_16_menu_item_info(
mi: &root::MENUITEMINFO,
) -> winapi::um::winuser::MENUITEMINFOW {
winapi::um::winuser::MENUITEMINFOW {
cbSize: std::mem::size_of::<winapi::um::winuser::MENUITEMINFOW>() as _,
fMask: mi.fMask,
fType: mi.fType,
fState: mi.fState,
wID: mi.wID,
hSubMenu: mi.hSubMenu as _,
hbmpChecked: mi.hbmpChecked as _,
hbmpUnchecked: mi.hbmpUnchecked as _,
dwItemData: mi.dwItemData,
dwTypeData: std::ptr::null_mut(),
cch: mi.cch as _,
hbmpItem: mi.hbmpItem as _,
}
}
#[cfg(target_family = "windows")]
fn menu_item_needs_string_conversion(mi: root::MENUITEMINFO) -> bool {
use crate::raw;
(mi.fMask & raw::MIIM_TYPE) != 0 && (mi.fMask & raw::MIIM_DATA) != 0
}