rust-cgui 0.1.15

A low-level cross-platform GUI library
Documentation

pub use super::*;

use std::sync::atomic::{AtomicUsize, Ordering};

static hDibDC_PTR_GLOBAL: AtomicUsize = AtomicUsize::new(0);
static GLOBAL_WIN_WIDTH: AtomicUsize = AtomicUsize::new(300);
static GLOBAL_WIN_HEIGHT: AtomicUsize = AtomicUsize::new(200);
// 0 == no change, 1 == repaint everything
static PLEASE_INVALIDATE_FLAG: AtomicUsize = AtomicUsize::new(0);

unsafe extern "system" fn some_experiment(hWnd: winapi::shared::windef::HWND,
                  msg: winapi::shared::minwindef::UINT,
                  wParam: winapi::shared::minwindef::WPARAM,
                  lParam: winapi::shared::minwindef::LPARAM) -> winapi::shared::minwindef::LRESULT {
  if msg == winapi::um::winuser::WM_PAINT {
    println!("Painting!");
    let mut paint: winapi::um::winuser::PAINTSTRUCT = mem::uninitialized();
    // Convert atomic int to pointer value
    println!(" !! hDibDC_PTR_GLOBAL.load(Ordering::SeqCst) = {}", hDibDC_PTR_GLOBAL.load(Ordering::SeqCst));
    let hDibDC_PTR: winapi::shared::windef::HDC = hDibDC_PTR_GLOBAL.load(Ordering::SeqCst) as winapi::shared::windef::HDC;
    
    let hw_dc = winapi::um::winuser::BeginPaint(hWnd, &mut paint);
    winapi::um::wingdi::BitBlt(
      hw_dc,
      0, 0, GLOBAL_WIN_WIDTH.load(Ordering::SeqCst) as i32 /* Width of DIB */, GLOBAL_WIN_HEIGHT.load(Ordering::SeqCst) as i32 /* Height of DIB */,
      hDibDC_PTR, 0, 0, winapi::um::wingdi::SRCCOPY
    );
    winapi::um::winuser::EndPaint(hWnd, &mut paint);
  }
  else if PLEASE_INVALIDATE_FLAG.load(Ordering::SeqCst) == 1 {
    // Invalidate entire window on every chardown
    let rect = winapi::shared::windef::RECT {
      left: 0, top: 0,
      right: GLOBAL_WIN_WIDTH.load(Ordering::SeqCst) as i32, bottom: GLOBAL_WIN_HEIGHT.load(Ordering::SeqCst) as i32
    };
    winapi::um::winuser::InvalidateRect(hWnd, &rect, winapi::shared::minwindef::FALSE);
  }
  return DefWindowProcW(hWnd, msg, wParam, lParam);
}

/// Win32 backend implementation
impl RenderBack for CWin {
  fn init(&mut self) {
    // Copy width/height to globals for async win32 BS callback
    GLOBAL_WIN_WIDTH.store(self.rep.width as usize, Ordering::SeqCst);
    GLOBAL_WIN_HEIGHT.store(self.rep.height as usize, Ordering::SeqCst);
    
    let name = win32_string(&self.rep.title);
    let title = win32_string(&self.rep.title);
    let res: Result<Window, Error> = unsafe {
      let hinstance = GetModuleHandleW( null_mut() );
      let wnd_class = WNDCLASSW {
        style : CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
        //lpfnWndProc : Some( DefWindowProcW ),
        lpfnWndProc : Some( some_experiment ),
        hInstance : hinstance,
        lpszClassName : name.as_ptr(),
        cbClsExtra : 0,
        cbWndExtra : 0,
        hIcon: null_mut(),
        hCursor: null_mut(),
        hbrBackground: null_mut(),
        lpszMenuName: null_mut(),
      };

      RegisterClassW( &wnd_class );

      let handle = CreateWindowExW(
        0,
        name.as_ptr(),
        title.as_ptr(),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT, // x
        CW_USEDEFAULT, // y
        self.rep.width as i32,
        self.rep.height as i32,
        null_mut(),
        null_mut(),
        hinstance,
        null_mut() );

      if handle.is_null() {
        Err( Error::last_os_error() )
      } else {
        Ok( Window { handle } )
      }
    };
    match res {
      Err(_) => {
        println!("CGui: Fatal error, cannot create win32 window object");
      }
      Ok (win_ptr) => {
        self.rep.win32_win_ptr = win_ptr;
      }
    }
  }
  
  
  fn redraw_dirty(&mut self) {
    let x1 = self.rep.dirty_extents[0];
    let y1 = self.rep.dirty_extents[1];
    let x2 = self.rep.dirty_extents[2];
    let y2 = self.rep.dirty_extents[3];
    self.redraw_box(x1, y1, x2, y2);
    self.rep.dirty_extents = [self.rep.width, self.rep.height, 0, 0]; // reset to inverted rectangle
  }
  
  
  fn redraw_box(&mut self, x1: usize, y1: usize, x2: usize, y2: usize) {
    if x1 == x2 || y1 == y2 {
      // Width or height = 0, no-op
      return;
    }
    if x1 > x2 || y1 > y2 {
      // Inverted extents, no-op
      return;
    }
    self.rep.redraw_box(x1, y1, x2, y2);
  }
  
  
  /// Apps which call event_tick manually MUST call init() first. I refuse to throw a check for self.init every time the user moves their mouse.
  fn event_tick(&self) -> String {
    let evt_str : String = unsafe {
      let mut message : MSG = mem::uninitialized();
      if GetMessageW( &mut message as *mut MSG, self.rep.win32_win_ptr.handle, 0, 0 ) > 0 {
        TranslateMessage( &message as *const MSG );
        DispatchMessageW( &message as *const MSG );
        
        if message.message == 258 {
          // indicates key down. if char is ASCII-like it seems to be stored in message.wParam
          let possible_char : char = message.wParam as u8 as char;
          if possible_char.is_alphabetic() || possible_char.is_numeric() {
            format!("KeyPress,{}", possible_char)
          }
          else {
            //println!("BIG 'ol TODO: implement control and/or modifier key input under windorks32");
            // //self.call_all_callbacks(Event::ControlChar(possible_char));
            "UNKNOWN_EVT".to_string()
          }
        }
        else if message.message == /*160*/ 161 {
          // Looks like a window resize and/or mapping event
          "WinShown".to_string()
        }
        else if message.message == winapi::um::winuser::WM_PAINT {
          "WM_PAINT".to_string()
        }
        else {
          
          // println!("Unhandled message state details:");
          // println!("  message.message = {}", message.message);
          // println!("  message.wParam = {}", message.wParam);
          // println!("  message.lParam = {}", message.lParam);
          // println!("  message.pt.x = {}", message.pt.x);
          // println!("  message.pt.y = {}", message.pt.y);
          
          "UNKNOWN_EVT".to_string()
        }
      }
      else {
        "WinClose".to_string() // IDK, probably?
      }
    };
    return evt_str;
  }
  fn event_loop(&mut self) {
    if ! self.rep.init_flag {
      self.init();
    }
    while ! self.rep.exit_flag {
      let evt_mess = self.event_tick();
      self.call_callbacks(evt_mess);
    }
  }
}

// Helper struct - really just renaming HWND?
// Kinda confusing, but I'm going off 3am coffee and 6-year-old blog posts.
pub struct Window {
  handle : HWND,
}

// Another helper
fn win32_string( value : &str ) -> Vec<u16> {
  OsStr::new( value ).encode_wide().chain( once( 0 ) ).collect()
}

impl RenderBack for CWinRep {
  fn init(&mut self) {
    println!("CWinRep has no need to have the RenderBack::init() function called");
  }
  
  fn redraw_dirty(&mut self) {
    let x1 = self.dirty_extents[0];
    let y1 = self.dirty_extents[1];
    let x2 = self.dirty_extents[2];
    let y2 = self.dirty_extents[3];
    self.redraw_box(x1, y1, x2, y2);
    self.dirty_extents = [self.width, self.height, 0, 0]; // reset to inverted rectangle
  }
  
  
  fn redraw_box(&mut self, x1: usize, y1: usize, x2: usize, y2: usize) {
    if x1 == x2 || y1 == y2 {
      // Width or height = 0, no-op
      return;
    }
    if x1 > x2 || y1 > y2 {
      // Inverted extents, no-op
      return;
    }
    unsafe {
      // https://docs.rs/winapi/*/x86_64-pc-windows-msvc/winapi/?search=
      
      //let hdc: winapi::shared::windef::HDC = winapi::um::winuser::GetDC(self.win32_win_ptr.handle);
      // Loop over x1-x2, y1-y2 drawing single pixels. Not very fast.
      // for x in x1..x2 {
      //   for y in y1..y2 {
      //     let color = self.pixels.get_pixel(x as u32, y as u32).to_rgb().data;
      //     let color_int: u32 = // Windorks uses 0x00bbggrr (https://docs.microsoft.com/en-us/windows/desktop/gdi/colorref)
      //       ((color[2] as u32) << 16) |
      //       ((color[1] as u32) << 8) |
      //       ((color[0] as u32) << 0)
      //       ;
      //     winapi::um::wingdi::SetPixelV(hdc, x as i32, y as i32, color_int);
      //     //println!("color = {}, {}, {}", color[0], color[1], color[2]);
      //     //println!("color_int = {}", color_int);
      //   }
      // }
      
      let mut bitmapinfo: winapi::um::wingdi::BITMAPINFO = mem::zeroed();
      bitmapinfo.bmiHeader.biSize = mem::size_of::<winapi::um::wingdi::BITMAPINFOHEADER>() as u32;
      //println!("bitmapinfo.bmiHeader.biSize={}", bitmapinfo.bmiHeader.biSize); // 40
      bitmapinfo.bmiHeader.biWidth = self.width as i32;
      bitmapinfo.bmiHeader.biHeight = self.height as i32;
      bitmapinfo.bmiHeader.biPlanes = 1;
      bitmapinfo.bmiHeader.biBitCount = 32;
      bitmapinfo.bmiHeader.biCompression = winapi::um::wingdi::BI_RGB;
      
      let mut null_p: *mut winapi::ctypes::c_void = mem::zeroed();
      
      //let heap_pixmem: Box<u8> = Box::new(self.width * self.height * 4 /* 4 bytes/px */);
      //let heap_pixmem: Box<Vec<u8>> = Box::from_raw(vec![0; self.width * self.height * 4 /* 4 bytes/px */ ].into_boxed_slice() .as_mut_ptr() as * mut _);
      //let heap_pixmem_ptr = Box::leak(heap_pixmem);
      
      //let heap_pixmem: *mut libc::c_void = libc::malloc(self.width * self.height * 4 /* 4 bytes/px */);
      
      let mut ppvBits: *mut winapi::ctypes::c_void = mem::zeroed();
      println!("ppvBits norm={:?}", ppvBits);
      println!("ppvBits={:p}", ppvBits);
      //println!("*ppvBits={:p}", *ppvBits); // dereference of null pointer b/c has no value
      
      let mem_hdc = winapi::um::winuser::GetDC(winapi::um::winuser::GetDesktopWindow());
      println!("mem_hdc={:p}   {:?}", mem_hdc, mem_hdc);
      let hbitmap = winapi::um::wingdi::CreateDIBSection(
        mem_hdc,
        &bitmapinfo,
        winapi::um::wingdi::DIB_RGB_COLORS,
        &mut ppvBits,
        null_p, 0, // section, offser
      );
      println!("hbitmap={:p}   {:?}", hbitmap, hbitmap);
      //let last_e = winapi::um::errhandlingapi::GetLastError();
      //println!("GetLastError()={}", last_e);
      
      // ptr_to_pixmem now points to a pointer to a block of memory we can copy data TO
      let h_dib_dc: winapi::shared::windef::HDC = winapi::um::wingdi::CreateCompatibleDC(mem_hdc);
      // Convert pointer to usize
      hDibDC_PTR_GLOBAL.store(h_dib_dc as usize, Ordering::SeqCst);
      
      println!(" !! h_dib_dc={:?}", h_dib_dc);
      let hOldObj = winapi::um::wingdi::SelectObject(h_dib_dc, hbitmap as *mut winapi::ctypes::c_void);
      println!("hOldObj={:?}", hOldObj);
      
      println!("ppvBits norm={:?}", ppvBits);
      println!("ppvBits={:p} (should be non-zero)", ppvBits);
      
      {
        // Now we can modify b/c we called winapi::um::wingdi::SelectObject
        
        let mut ppvBits: *mut u8 = ppvBits as *mut u8;
        println!("ppvBits={:p} (should be non-zero)", ppvBits);
        
        for x in 0..self.width {
          for y in 0..self.height {
            let color = self.pixels.get_pixel(x as u32, y as u32).to_rgb().data;
            // let color_int: u32 = // Windorks uses 0x00bbggrr (https://docs.microsoft.com/en-us/windows/desktop/gdi/colorref)
            //   ((color[2] as u32) << 16) |
            //   ((color[1] as u32) << 8) |
            //   ((color[0] as u32) << 0)
            //   ;
            
            //let offset = (((x*self.width) + y) * 3) as isize; // origin appears at bottom-left
            //let offset = (((y*self.height) + x) * 3) as isize; // this draws black top, white bottom and inverses
            
            //let offset = ((( ((self.width-1)-x) *self.width) + y) * 3) as isize; // origin back at 0,0 but axis flipped
            
            let offset = ((( ((self.width-1)-y) * self.width) + x) * 3) as isize;
            
            //if offset + 3 > (self.width*self.height) as isize {
            //  continue; // dumb off-by-one i don't care about while testing
            //}
            
            //ptr::write(ppvBits.offset(offset+0), 0);
            ptr::write(ppvBits.offset(offset+0), color[0]);
            ptr::write(ppvBits.offset(offset+1), color[1]);
            ptr::write(ppvBits.offset(offset+2), color[2]);
            
            // 0,0,0 == black
            // 0,255,0 == grey stripes
            // 0,0,255 == grey stripes
            // 127,127,127 == solid grey and dashed black?
            
            // ptr::write(ppvBits.offset(offset+0), 0);
            // ptr::write(ppvBits.offset(offset+1), 128);
            // ptr::write(ppvBits.offset(offset+2), 128);
            
          }
        }
      }
      
      // signal to win callback to invalidate pixels at earliest convinience
      PLEASE_INVALIDATE_FLAG.store(1, Ordering::SeqCst);
      
      winapi::um::winuser::ReleaseDC(winapi::um::winuser::GetDesktopWindow(), h_dib_dc);
      winapi::um::winuser::ReleaseDC(winapi::um::winuser::GetDesktopWindow(), mem_hdc);
      
    }
    
  }
  
  
  /// Apps which call event_tick manually MUST call init() first. I refuse to throw a check for self.init every time the user moves their mouse.
  fn event_tick(&self) -> String {
    let evt_str : String = unsafe {
      let mut message : MSG = mem::uninitialized();
      if GetMessageW( &mut message as *mut MSG, self.win32_win_ptr.handle, 0, 0 ) > 0 {
        TranslateMessage( &message as *const MSG );
        DispatchMessageW( &message as *const MSG );
        
        if message.message == 258 {
          // indicates key down. if char is ASCII-like it seems to be stored in message.wParam
          let possible_char : char = message.wParam as u8 as char;
          if possible_char.is_alphabetic() || possible_char.is_numeric() {
            format!("KeyPress,{}", possible_char)
          }
          else {
            //println!("BIG 'ol TODO: implement control and/or modifier key input under windorks32");
            // //self.call_all_callbacks(Event::ControlChar(possible_char));
            "UNKNOWN_EVT".to_string()
          }
        }
        else if message.message == /*160*/ 161 {
          // Looks like a window resize and/or mapping event
          "WinShown".to_string()
        }
        else {
          
          // println!("Unhandled message state details:");
          // println!("  message.message = {}", message.message);
          // println!("  message.wParam = {}", message.wParam);
          // println!("  message.lParam = {}", message.lParam);
          // println!("  message.pt.x = {}", message.pt.x);
          // println!("  message.pt.y = {}", message.pt.y);
          
          "UNKNOWN_EVT".to_string()
        }
      }
      else {
        "WinClose".to_string() // IDK, probably?
      }
    };
    return evt_str;
  }
  fn event_loop(&mut self) {
    
  }
}