[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/github-roushan/RustPython/main/stdlib/src/tkinter.rs [Back]  [Original]

// cspell:ignore createcommand

pub(crate) use self::_tkinter::make_module;

#[pymodule]
mod _tkinter {
    use rustpython_vm::types::Constructor;
    use rustpython_vm::{PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine};

    use rustpython_vm::builtins::{PyInt, PyStr, PyType};
    use std::{ffi, ptr};

    use crate::builtins::PyTypeRef;
    use rustpython_common::atomic::AtomicBool;
    use rustpython_common::atomic::Ordering;

    #[cfg(windows)]
    fn _get_tcl_lib_path() -> String {
        // TODO: fix packaging
        String::from(r"C:\ActiveTcl\lib")
    }

    #[pyattr(name = "TclError", once)]
    fn tcl_error(vm: &VirtualMachine) -> PyTypeRef {
        vm.ctx.new_exception_type(
            "_tkinter",
            "TclError",
            Some(vec![vm.ctx.exceptions.exception_type.to_owned()]),
        )
    }

    #[pyattr(name = "TkError", once)]
    fn tk_error(vm: &VirtualMachine) -> PyTypeRef {
        vm.ctx.new_exception_type(
            "_tkinter",
            "TkError",
            Some(vec![vm.ctx.exceptions.exception_type.to_owned()]),
        )
    }

    #[pyattr(once, name = "TK_VERSION")]
    fn tk_version(_vm: &VirtualMachine) -> String {
        format!("{}.{}", 8, 6)
    }

    #[pyattr(once, name = "TCL_VERSION")]
    fn tcl_version(_vm: &VirtualMachine) -> String {
        format!(
            "{}.{}",
            tk_sys::TCL_MAJOR_VERSION,
            tk_sys::TCL_MINOR_VERSION
        )
    }

    #[pyattr]
    #[pyclass(name = "TclObject")]
    #[derive(PyPayload)]
    struct TclObject {
        value: *mut tk_sys::Tcl_Obj,
    }

    impl std::fmt::Debug for TclObject {
        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            write!(f, "TkApp")
        }
    }

    #[derive(FromArgs, Debug)]
    struct TkAppConstructorArgs {
        #[pyarg(any)]
        screen_name: Option,
        #[pyarg(any)]
        _base_name: Option,
        #[pyarg(any)]
        class_name: String,
        #[pyarg(any)]
        interactive: i32,
        #[pyarg(any)]
        wantobjects: i32,
        #[pyarg(any, default = "true")]
        want_tk: bool,
        #[pyarg(any)]
        sync: i32,
        #[pyarg(any)]
        use_: Option,
    }

    impl Constructor for TkApp {
        type Args = TkAppConstructorArgs;

        fn py_new(
            _zelf: PyRef,
            args: Self::Args,
            vm: &VirtualMachine,
        ) -> PyResult {
            create(args, vm)
        }
    }

    fn varname_converter(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
        // if let Ok(bytes) = obj.bytes(vm) {
        //     todo!()
        // }

        // str
        if let Some(str) = obj.downcast_ref::() {
            return Ok(str.as_str().to_string());
        }

        if let Some(_tcl_obj) = obj.downcast_ref::() {
            // Assume that the Tcl object has a method to retrieve a string.
            // return tcl_obj.
            todo!();
        }

        // Construct an error message using the type name (truncated to 50 characters).
        Err(vm.new_type_error(format!(
            "must be str, bytes or Tcl_Obj, not {:.50}",
            obj.obj_type().str(vm)?.as_str()
        )))
    }

    // TODO: DISALLOW_INSTANTIATION
    #[pyclass(with(Constructor))]
    impl TkApp {
        fn from_bool(&self, obj: *mut tk_sys::Tcl_Obj) -> bool {
            let mut res = -1;
            unsafe {
                if tk_sys::Tcl_GetBooleanFromObj(self.interpreter, obj, &mut res)
                    != tk_sys::TCL_OK as i32
                {
                    panic!("Tcl_GetBooleanFromObj failed");
                }
            }
            assert!(res == 0 || res == 1);
            res != 0
        }

        fn from_object(
            &self,
            obj: *mut tk_sys::Tcl_Obj,
            vm: &VirtualMachine,
        ) -> PyResult {
            let type_ptr = unsafe { (*obj).typePtr };
            if type_ptr == ptr::null() {
                return self.unicode_from_object(obj, vm);
            } else if type_ptr == self.old_boolean_type || type_ptr == self.boolean_type {
                return Ok(vm.ctx.new_bool(self.from_bool(obj)).into());
            } else if type_ptr == self.string_type
                || type_ptr == self.utf32_string_type
                || type_ptr == self.pixel_type
            {
                return self.unicode_from_object(obj, vm);
            }
            // TODO: handle other types

            return Ok(TclObject { value: obj }.into_pyobject(vm));
        }

        fn unicode_from_string(
            s: *mut ffi::c_char,
            size: usize,
            vm: &VirtualMachine,
        ) -> PyResult {
            // terribly unsafe
            let s = unsafe { std::slice::from_raw_parts(s, size) }
                .to_vec()
                .into_iter()
                .map(|c| c as u8)
                .collect::();
            let s = String::from_utf8(s).unwrap();
            Ok(PyObjectRef::from(vm.ctx.new_str(s)))
        }

        fn unicode_from_object(
            &self,
            obj: *mut tk_sys::Tcl_Obj,
            vm: &VirtualMachine,
        ) -> PyResult {
            let type_ptr = unsafe { (*obj).typePtr };
            if type_ptr != ptr::null()
                && self.interpreter != ptr::null_mut()
                && (type_ptr == self.string_type || type_ptr == self.utf32_string_type)
            {
                let len = ptr::null_mut();
                let data = unsafe { tk_sys::Tcl_GetUnicodeFromObj(obj, len) };
                return if size_of::() == 2 {
                    let v = unsafe { std::slice::from_raw_parts(data as *const u16, len as usize) };
                    let s = String::from_utf16(v).unwrap();
                    Ok(PyObjectRef::from(vm.ctx.new_str(s)))
                } else {
                    let v = unsafe { std::slice::from_raw_parts(data as *const u32, len as usize) };
                    let s = widestring::U32String::from_vec(v).to_string_lossy();
                    Ok(PyObjectRef::from(vm.ctx.new_str(s)))
                };
            }
            let len = ptr::null_mut();
            let s = unsafe { tk_sys::Tcl_GetStringFromObj(obj, len) };
            Self::unicode_from_string(s, len as _, vm)
        }

        #[pymethod]
        fn getvar(&self, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult {
            // TODO: technically not thread safe
            let name = varname_converter(arg, vm)?;

            let res = unsafe {
                tk_sys::Tcl_GetVar2Ex(
                    self.interpreter,
                    ptr::null(),
                    name.as_ptr() as _,
                    tk_sys::TCL_LEAVE_ERR_MSG as _,
                )
            };
            if res == ptr::null_mut() {
                todo!();
            }
            let res = if self.want_objects {
                self.from_object(res, vm)
            } else {
                self.unicode_from_object(res, vm)
            }?;
            Ok(res)
        }

        #[pymethod]
        fn getint(&self, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult {
            if let Some(int) = arg.downcast_ref::() {
                return Ok(PyObjectRef::from(vm.ctx.new_int(int.as_bigint().clone())));
            }

            if let Some(obj) = arg.downcast_ref::() {
                let value = obj.value;
                unsafe { tk_sys::Tcl_IncrRefCount(value) };
            } else {
                todo!();
            }
            todo!();
        }
        // TODO: Fix arguments
        #[pymethod]
        fn mainloop(&self, threshold: Option) -> PyResult {
            let threshold = threshold.unwrap_or(0);
            todo!();
        }

        #[pymethod]
        fn quit(&self) {
            QUIT_MAIN_LOOP.store(true, Ordering::Relaxed);
        }
    }

    #[pyfunction]
    fn create(args: TkAppConstructorArgs, vm: &VirtualMachine) -> PyResult {
        unsafe {
            let interp = tk_sys::Tcl_CreateInterp();
            let want_objects = args.wantobjects != 0;
            let threaded = {
                let part1 = String::from("tcl_platform");
                let part2 = String::from("threaded");
                let part1_ptr = part1.as_ptr();
                let part2_ptr = part2.as_ptr();
                tk_sys::Tcl_GetVar2Ex(
                    interp,
                    part1_ptr as _,
                    part2_ptr as _,
                    tk_sys::TCL_GLOBAL_ONLY as ffi::c_int,
                )
            } != ptr::null_mut();
            let thread_id = tk_sys::Tcl_GetCurrentThread();
            let dispatching = false;
            let trace = None;
            // TODO: Handle threaded build
            let bool_str = String::from("oldBoolean");
            let old_boolean_type = tk_sys::Tcl_GetObjType(bool_str.as_ptr() as _);
            let (boolean_type, byte_array_type) = {
                let true_str = String::from("true");
                let mut value = *tk_sys::Tcl_NewStringObj(true_str.as_ptr() as _, -1);
                let mut bool_value = 0;
                tk_sys::Tcl_GetBooleanFromObj(interp, &mut value, &mut bool_value);
                let boolean_type = value.typePtr;
                tk_sys::Tcl_DecrRefCount(&mut value);

                let mut value =
                    *tk_sys::Tcl_NewByteArrayObj(&bool_value as *const i32 as *const u8, 1);
                let byte_array_type = value.typePtr;
                tk_sys::Tcl_DecrRefCount(&mut value);
                (boolean_type, byte_array_type)
            };
            let double_str = String::from("double");
            let double_type = tk_sys::Tcl_GetObjType(double_str.as_ptr() as _);
            let int_str = String::from("int");
            let int_type = tk_sys::Tcl_GetObjType(int_str.as_ptr() as _);
            let int_type = if int_type == ptr::null() {
                let mut value = *tk_sys::Tcl_NewIntObj(0);
                let res = value.typePtr;
                tk_sys::Tcl_DecrRefCount(&mut value);
                res
            } else {
                int_type
            };
            let wide_int_str = String::from("wideInt");
            let wide_int_type = tk_sys::Tcl_GetObjType(wide_int_str.as_ptr() as _);
            let bignum_str = String::from("bignum");
            let bignum_type = tk_sys::Tcl_GetObjType(bignum_str.as_ptr() as _);
            let list_str = String::from("list");
            let list_type = tk_sys::Tcl_GetObjType(list_str.as_ptr() as _);
            let string_str = String::from("string");
            let string_type = tk_sys::Tcl_GetObjType(string_str.as_ptr() as _);
            let utf32_str = String::from("utf32");
            let utf32_string_type = tk_sys::Tcl_GetObjType(utf32_str.as_ptr() as _);
            let pixel_str = String::from("pixel");
            let pixel_type = tk_sys::Tcl_GetObjType(pixel_str.as_ptr() as _);

            let exit_str = String::from("exit");
            tk_sys::Tcl_DeleteCommand(interp, exit_str.as_ptr() as _);

            if let Some(name) = args.screen_name {
                tk_sys::Tcl_SetVar2(
                    interp,
                    "env".as_ptr() as _,
                    "DISPLAY".as_ptr() as _,
                    name.as_ptr() as _,
                    tk_sys::TCL_GLOBAL_ONLY as i32,
                );
            }

            if args.interactive != 0 {
                tk_sys::Tcl_SetVar(
                    interp,
                    "tcl_interactive".as_ptr() as _,
                    "1".as_ptr() as _,
                    tk_sys::TCL_GLOBAL_ONLY as i32,
                );
            } else {
                tk_sys::Tcl_SetVar(
                    interp,
                    "tcl_interactive".as_ptr() as _,
                    "0".as_ptr() as _,
                    tk_sys::TCL_GLOBAL_ONLY as i32,
                );
            }

            let argv0 = args.class_name.clone().to_lowercase();
            tk_sys::Tcl_SetVar(
                interp,
                "argv0".as_ptr() as _,
                argv0.as_ptr() as _,
                tk_sys::TCL_GLOBAL_ONLY as i32,
            );

            if !args.want_tk {
                tk_sys::Tcl_SetVar(
                    interp,
                    "_tkinter_skip_tk_init".as_ptr() as _,
                    "1".as_ptr() as _,
                    tk_sys::TCL_GLOBAL_ONLY as i32,
                );
            }

            if args.sync != 0 || args.use_.is_some() {
                let mut argv = String::with_capacity(4);
                if args.sync != 0 {
                    argv.push_str("-sync");
                }
                if args.use_.is_some() {
                    if args.sync != 0 {
                        argv.push(' ');
                    }
                    argv.push_str("-use ");
                    argv.push_str(&args.use_.unwrap());
                }
                argv.push_str("\0");
                let argv_ptr = argv.as_ptr() as *mut *mut i8;
                tk_sys::Tcl_SetVar(
                    interp,
                    "argv".as_ptr() as _,
                    argv_ptr as *const i8,
                    tk_sys::TCL_GLOBAL_ONLY as i32,
                );
            }

            #[cfg(windows)]
            {
                let ret = std::env::var("TCL_LIBRARY");
                if ret.is_err() {
                    let loc = _get_tcl_lib_path();
                    std::env::set_var("TCL_LIBRARY", loc);
                }
            }

            // Bindgen cannot handle Tcl_AppInit
            if tk_sys::Tcl_Init(interp) != tk_sys::TCL_OK as ffi::c_int {
                todo!("Tcl_Init failed");
            }

            Ok(TkApp {
                interpreter: interp,
                want_objects,
                threaded,
                thread_id: Some(thread_id),
                dispatching,
                trace,
                old_boolean_type,
                boolean_type,
                byte_array_type,
                double_type,
                int_type,
                wide_int_type,
                bignum_type,
                list_type,
                string_type,
                utf32_string_type,
                pixel_type,
            }
            .into_pyobject(vm))
        }
    }

    #[pyattr]
    const READABLE: i32 = tk_sys::TCL_READABLE as i32;
    #[pyattr]
    const WRITABLE: i32 = tk_sys::TCL_WRITABLE as i32;
    #[pyattr]
    const EXCEPTION: i32 = tk_sys::TCL_EXCEPTION as i32;

    #[pyattr]
    const TIMER_EVENTS: i32 = tk_sys::TCL_TIMER_EVENTS as i32;
    #[pyattr]
    const IDLE_EVENTS: i32 = tk_sys::TCL_IDLE_EVENTS as i32;
    #[pyattr]
    const DONT_WAIT: i32 = tk_sys::TCL_DONT_WAIT as i32;
}

Web Proxy Viewer  |  New URL  |  Original Page