From 8a81650b3da7ce00725336df9e03b38e935c5a65 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Jul 2014 22:53:03 -0400 Subject: Moved it all back; the preemptive multitaksing during an event handler kills us on all platforms. Going to have to restrict ALL GUI accss to happening from one t hread, so going to need to drop uitask entirely and have just a start() callback for startup code and a post() function for posting requests to windows (like channel sends but into a perpetual buffer). --- prevlib/sysdata_windows.go | 525 --------------------------------------------- 1 file changed, 525 deletions(-) delete mode 100644 prevlib/sysdata_windows.go (limited to 'prevlib/sysdata_windows.go') diff --git a/prevlib/sysdata_windows.go b/prevlib/sysdata_windows.go deleted file mode 100644 index 1d3541a..0000000 --- a/prevlib/sysdata_windows.go +++ /dev/null @@ -1,525 +0,0 @@ -// 11 february 2014 - -package ui - -import ( - "fmt" - "sync" - "syscall" - "unsafe" -) - -type sysData struct { - cSysData - - hwnd _HWND - children map[_HMENU]*sysData - nextChildID _HMENU - childrenLock sync.Mutex - isMarquee bool // for sysData.setProgress() - // unlike with GTK+ and Mac OS X, we're responsible for sizing Area properly ourselves - areawidth int - areaheight int - clickCounter clickCounter - lastfocus _HWND -} - -type classData struct { - name *uint16 - style uint32 - xstyle uint32 - altStyle uint32 - storeSysData bool - doNotLoadFont bool - appendMsg uintptr - insertBeforeMsg uintptr - deleteMsg uintptr - selectedIndexMsg uintptr - selectedIndexErr uintptr - addSpaceErr uintptr - lenMsg uintptr -} - -const controlstyle = _WS_CHILD | _WS_VISIBLE | _WS_TABSTOP -const controlxstyle = 0 - -var classTypes = [nctypes]*classData{ - c_window: &classData{ - name: stdWndClass, - style: _WS_OVERLAPPEDWINDOW, - xstyle: 0, - storeSysData: true, - doNotLoadFont: true, - }, - c_button: &classData{ - name: toUTF16("BUTTON"), - style: _BS_PUSHBUTTON | controlstyle, - xstyle: 0 | controlxstyle, - }, - c_checkbox: &classData{ - name: toUTF16("BUTTON"), - // don't use BS_AUTOCHECKBOX because http://blogs.msdn.com/b/oldnewthing/archive/2014/05/22/10527522.aspx - style: _BS_CHECKBOX | controlstyle, - xstyle: 0 | controlxstyle, - }, - c_combobox: &classData{ - name: toUTF16("COMBOBOX"), - style: _CBS_DROPDOWNLIST | _WS_VSCROLL | controlstyle, - xstyle: 0 | controlxstyle, - altStyle: _CBS_DROPDOWN | _CBS_AUTOHSCROLL | _WS_VSCROLL | controlstyle, - appendMsg: _CB_ADDSTRING, - insertBeforeMsg: _CB_INSERTSTRING, - deleteMsg: _CB_DELETESTRING, - selectedIndexMsg: _CB_GETCURSEL, - selectedIndexErr: negConst(_CB_ERR), - addSpaceErr: negConst(_CB_ERRSPACE), - lenMsg: _CB_GETCOUNT, - }, - c_lineedit: &classData{ - name: toUTF16("EDIT"), - // WS_EX_CLIENTEDGE without WS_BORDER will apply visual styles - // thanks to MindChild in irc.efnet.net/#winprog - style: _ES_AUTOHSCROLL | controlstyle, - xstyle: _WS_EX_CLIENTEDGE | controlxstyle, - altStyle: _ES_PASSWORD | _ES_AUTOHSCROLL | controlstyle, - }, - c_label: &classData{ - name: toUTF16("STATIC"), - // SS_NOPREFIX avoids accelerator translation; SS_LEFTNOWORDWRAP clips text past the end - // controls are vertically aligned to the top by default (thanks Xeek in irc.freenode.net/#winapi) - // also note that tab stops are remove dfor labels - style: (_SS_NOPREFIX | _SS_LEFTNOWORDWRAP | controlstyle) &^ _WS_TABSTOP, - xstyle: 0 | controlxstyle, - // MAKE SURE THIS IS THE SAME - altStyle: (_SS_NOPREFIX | _SS_LEFTNOWORDWRAP | controlstyle) &^ _WS_TABSTOP, - }, - c_listbox: &classData{ - name: toUTF16("LISTBOX"), - // we don't use LBS_STANDARD because it sorts (and has WS_BORDER; see above) - // LBS_NOINTEGRALHEIGHT gives us exactly the size we want - // LBS_MULTISEL sounds like it does what we want but it actually doesn't; instead, it toggles item selection regardless of modifier state, which doesn't work like anything else (see http://msdn.microsoft.com/en-us/library/windows/desktop/bb775149%28v=vs.85%29.aspx and http://msdn.microsoft.com/en-us/library/windows/desktop/aa511485.aspx) - style: _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle, - xstyle: _WS_EX_CLIENTEDGE | controlxstyle, - altStyle: _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle, - appendMsg: _LB_ADDSTRING, - insertBeforeMsg: _LB_INSERTSTRING, - deleteMsg: _LB_DELETESTRING, - selectedIndexMsg: _LB_GETCURSEL, - selectedIndexErr: negConst(_LB_ERR), - addSpaceErr: negConst(_LB_ERRSPACE), - lenMsg: _LB_GETCOUNT, - }, - c_progressbar: &classData{ - name: toUTF16(x_PROGRESS_CLASS), - // note that tab stops are disabled for progress bars - style: (_PBS_SMOOTH | controlstyle) &^ _WS_TABSTOP, - xstyle: 0 | controlxstyle, - doNotLoadFont: true, - }, - c_area: &classData{ - name: areaWndClass, - style: areastyle, - xstyle: areaxstyle, - storeSysData: true, - doNotLoadFont: true, - }, -} - -func (s *sysData) addChild(child *sysData) _HMENU { - s.childrenLock.Lock() - defer s.childrenLock.Unlock() - s.nextChildID++ // start at 1 - if s.children == nil { - s.children = map[_HMENU]*sysData{} - } - s.children[s.nextChildID] = child - return s.nextChildID -} - -func (s *sysData) delChild(id _HMENU) { - s.childrenLock.Lock() - defer s.childrenLock.Unlock() - delete(s.children, id) -} - -var ( - _blankString = toUTF16("") - blankString = utf16ToArg(_blankString) -) - -func (s *sysData) make(window *sysData) (err error) { - ct := classTypes[s.ctype] - cid := _HMENU(0) - pwin := uintptr(_NULL) - if window != nil { // this is a child control - cid = window.addChild(s) - pwin = uintptr(window.hwnd) - } - style := uintptr(ct.style) - if s.alternate { - style = uintptr(ct.altStyle) - } - lpParam := uintptr(_NULL) - if ct.storeSysData { - lpParam = uintptr(unsafe.Pointer(s)) - } - r1, _, err := _createWindowEx.Call( - uintptr(ct.xstyle), - utf16ToArg(ct.name), - blankString, // we set the window text later - style, - negConst(_CW_USEDEFAULT), - negConst(_CW_USEDEFAULT), - negConst(_CW_USEDEFAULT), - negConst(_CW_USEDEFAULT), - pwin, - uintptr(cid), - uintptr(hInstance), - lpParam) - if r1 == 0 { // failure - if window != nil { - window.delChild(cid) - } - panic(fmt.Errorf("error actually creating window/control: %v", err)) - } - if !ct.storeSysData { // regular control; store s.hwnd ourselves - s.hwnd = _HWND(r1) - } else if s.hwnd != _HWND(r1) { // we store sysData in storeSysData(); sanity check - panic(fmt.Errorf("hwnd mismatch creating window/control: storeSysData() stored 0x%X but CreateWindowEx() returned 0x%X", s.hwnd, r1)) - } - if !ct.doNotLoadFont { - _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_WM_SETFONT), - uintptr(_WPARAM(controlFont)), - uintptr(_LPARAM(_TRUE))) - } - return nil -} - -var ( - _updateWindow = user32.NewProc("UpdateWindow") -) - -// if the object is a window, we need to do the following the first time -// ShowWindow(hwnd, nCmdShow); -// UpdateWindow(hwnd); -func (s *sysData) firstShow() error { - _showWindow.Call( - uintptr(s.hwnd), - uintptr(nCmdShow)) - r1, _, err := _updateWindow.Call(uintptr(s.hwnd)) - if r1 == 0 { // failure - panic(fmt.Errorf("error updating window for the first time: %v", err)) - } - return nil -} - -func (s *sysData) show() { - _showWindow.Call( - uintptr(s.hwnd), - uintptr(_SW_SHOW)) -} - -func (s *sysData) hide() { - _showWindow.Call( - uintptr(s.hwnd), - uintptr(_SW_HIDE)) -} - -func (s *sysData) setText(text string) { - ptext := toUTF16(text) - r1, _, err := _setWindowText.Call( - uintptr(s.hwnd), - utf16ToArg(ptext)) - if r1 == 0 { // failure - panic(fmt.Errorf("error setting window/control text: %v", err)) - } -} - -func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error { - r1, _, err := _moveWindow.Call( - uintptr(s.hwnd), - uintptr(x), - uintptr(y), - uintptr(width), - uintptr(height), - uintptr(_TRUE)) - if r1 == 0 { // failure - return fmt.Errorf("error setting window/control rect: %v", err) - } - return nil -} - -func (s *sysData) isChecked() bool { - r1, _, _ := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_BM_GETCHECK), - uintptr(0), - uintptr(0)) - return r1 == _BST_CHECKED -} - -func (s *sysData) text() (str string) { - var tc []uint16 - - r1, _, _ := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_WM_GETTEXTLENGTH), - uintptr(0), - uintptr(0)) - length := r1 + 1 // terminating null - tc = make([]uint16, length) - _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_WM_GETTEXT), - uintptr(_WPARAM(length)), - uintptr(_LPARAM(unsafe.Pointer(&tc[0])))) - return syscall.UTF16ToString(tc) -} - -func (s *sysData) append(what string) { - pwhat := toUTF16(what) - r1, _, err := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(classTypes[s.ctype].appendMsg), - uintptr(_WPARAM(0)), - utf16ToLPARAM(pwhat)) - if r1 == uintptr(classTypes[s.ctype].addSpaceErr) { - panic(fmt.Errorf("out of space adding item to combobox/listbox (last error: %v)", err)) - } else if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) { - panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", err)) - } -} - -func (s *sysData) insertBefore(what string, index int) { - pwhat := toUTF16(what) - r1, _, err := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(classTypes[s.ctype].insertBeforeMsg), - uintptr(_WPARAM(index)), - utf16ToLPARAM(pwhat)) - if r1 == uintptr(classTypes[s.ctype].addSpaceErr) { - panic(fmt.Errorf("out of space adding item to combobox/listbox (last error: %v)", err)) - } else if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) { - panic(fmt.Errorf("failed to add item to combobox/listbox (last error: %v)", err)) - } -} - -func (s *sysData) selectedIndex() int { - r1, _, _ := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(classTypes[s.ctype].selectedIndexMsg), - uintptr(_WPARAM(0)), - uintptr(_LPARAM(0))) - if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) { // no selection or manually entered text (apparently, for the latter) - return -1 - } - return int(r1) -} - -func (s *sysData) selectedIndices() []int { - if !s.alternate { // single-selection list box; use single-selection method - index := s.selectedIndex() - if index == -1 { - return nil - } - return []int{index} - } - - r1, _, err := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_LB_GETSELCOUNT), - uintptr(0), - uintptr(0)) - if r1 == negConst(_LB_ERR) { - panic(fmt.Errorf("error: LB_ERR from LB_GETSELCOUNT in what we know is a multi-selection listbox: %v", err)) - } - if r1 == 0 { // nothing selected - return nil - } - indices := make([]int, r1) - r1, _, err = _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_LB_GETSELITEMS), - uintptr(_WPARAM(r1)), - uintptr(_LPARAM(unsafe.Pointer(&indices[0])))) - if r1 == negConst(_LB_ERR) { - panic(fmt.Errorf("error: LB_ERR from LB_GETSELITEMS in what we know is a multi-selection listbox: %v", err)) - } - return indices -} - -func (s *sysData) selectedTexts() []string { - indices := s.selectedIndices() - strings := make([]string, len(indices)) - for i, v := range indices { - r1, _, err := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_LB_GETTEXTLEN), - uintptr(_WPARAM(v)), - uintptr(0)) - if r1 == negConst(_LB_ERR) { - panic(fmt.Errorf("error: LB_ERR from LB_GETTEXTLEN in what we know is a valid listbox index (came from LB_GETSELITEMS): %v", err)) - } - str := make([]uint16, r1) - r1, _, err = _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_LB_GETTEXT), - uintptr(_WPARAM(v)), - uintptr(_LPARAM(unsafe.Pointer(&str[0])))) - if r1 == negConst(_LB_ERR) { - panic(fmt.Errorf("error: LB_ERR from LB_GETTEXT in what we know is a valid listbox index (came from LB_GETSELITEMS): %v", err)) - } - strings[i] = syscall.UTF16ToString(str) - } - return strings -} - -func (s *sysData) setWindowSize(width int, height int) error { - var rect _RECT - - r1, _, err := _getClientRect.Call( - uintptr(s.hwnd), - uintptr(unsafe.Pointer(&rect))) - if r1 == 0 { - panic(fmt.Errorf("error getting upper-left of window for resize: %v", err)) - } - // TODO AdjustWindowRect() on the result - // 0 because (0,0) is top-left so no winheight - err = s.setRect(int(rect.left), int(rect.top), width, height, 0) - if err != nil { - panic(fmt.Errorf("error actually resizing window: %v", err)) - } - return nil -} - -func (s *sysData) delete(index int) { - r1, _, err := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(classTypes[s.ctype].deleteMsg), - uintptr(_WPARAM(index)), - uintptr(0)) - if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) { - panic(fmt.Errorf("failed to delete item from combobox/listbox (last error: %v)", err)) - } -} - -func (s *sysData) setIndeterminate() { - r1, _, err := _setWindowLongPtr.Call( - uintptr(s.hwnd), - negConst(_GWL_STYLE), - uintptr(classTypes[s.ctype].style | _PBS_MARQUEE)) - if r1 == 0 { - panic(fmt.Errorf("error setting progress bar style to enter indeterminate mode: %v", err)) - } - _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_PBM_SETMARQUEE), - uintptr(_WPARAM(_TRUE)), - uintptr(0)) - s.isMarquee = true -} - -func (s *sysData) setProgress(percent int) { - if percent == -1 { - s.setIndeterminate() - return - } - if s.isMarquee { - // turn off marquee before switching back - _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_PBM_SETMARQUEE), - uintptr(_WPARAM(_FALSE)), - uintptr(0)) - r1, _, err := _setWindowLongPtr.Call( - uintptr(s.hwnd), - negConst(_GWL_STYLE), - uintptr(classTypes[s.ctype].style)) - if r1 == 0 { - panic(fmt.Errorf("error setting progress bar style to leave indeterminate mode (percent %d): %v", percent, err)) - } - s.isMarquee = false - } - send := func(msg uintptr, n int, l _LPARAM) { - _sendMessage.Call( - uintptr(s.hwnd), - msg, - uintptr(_WPARAM(n)), - uintptr(l)) - } - // Windows 7 has a non-disableable slowly-animating progress bar increment - // there isn't one for decrement, so we'll work around by going one higher and then lower again - // for the case where percent == 100, we need to increase the range temporarily - // sources: http://social.msdn.microsoft.com/Forums/en-US/61350dc7-6584-4c4e-91b0-69d642c03dae/progressbar-disable-smooth-animation http://stackoverflow.com/questions/2217688/windows-7-aero-theme-progress-bar-bug http://discuss.joelonsoftware.com/default.asp?dotnet.12.600456.2 http://stackoverflow.com/questions/22469876/progressbar-lag-when-setting-position-with-pbm-setpos http://stackoverflow.com/questions/6128287/tprogressbar-never-fills-up-all-the-way-seems-to-be-updating-too-fast - if percent == 100 { - send(_PBM_SETRANGE32, 0, 101) - } - send(_PBM_SETPOS, percent+1, 0) - send(_PBM_SETPOS, percent, 0) - if percent == 100 { - send(_PBM_SETRANGE32, 0, 100) - } -} - -func (s *sysData) len() int { - r1, _, err := _sendMessage.Call( - uintptr(s.hwnd), - uintptr(classTypes[s.ctype].lenMsg), - uintptr(_WPARAM(0)), - uintptr(_LPARAM(0))) - if r1 == uintptr(classTypes[s.ctype].selectedIndexErr) { - panic(fmt.Errorf("unexpected error return from sysData.len(); GetLastError() says %v", err)) - } - return int(r1) -} - -func (s *sysData) setAreaSize(width int, height int) { - _sendMessage.Call( - uintptr(s.hwnd), - uintptr(msgSetAreaSize), - uintptr(width), // WPARAM is UINT_PTR on Windows XP and newer at least, so we're good with this - uintptr(height)) -} - -func (s *sysData) repaintAll() { - _sendMessage.Call( - uintptr(s.hwnd), - uintptr(msgRepaintAll), - uintptr(0), - uintptr(0)) -} - -func (s *sysData) center() { - var ws _RECT - - r1, _, err := _getWindowRect.Call( - uintptr(s.hwnd), - uintptr(unsafe.Pointer(&ws))) - if r1 == 0 { - panic(fmt.Errorf("error getting window rect for sysData.center(): %v", err)) - } - // TODO should this be using the monitor functions instead? http://blogs.msdn.com/b/oldnewthing/archive/2005/05/05/414910.aspx - // error returns from GetSystemMetrics() is meaningless because the return value, 0, is still valid - // TODO should this be using the client rect and not the window rect? - dw, _, _ := _getSystemMetrics.Call(uintptr(_SM_CXFULLSCREEN)) - dh, _, _ := _getSystemMetrics.Call(uintptr(_SM_CYFULLSCREEN)) - ww := ws.right - ws.left - wh := ws.bottom - ws.top - wx := (int32(dw) / 2) - (ww / 2) - wy := (int32(dh) / 2) - (wh / 2) - s.setRect(int(wx), int(wy), int(ww), int(wh), 0) -} - -func (s *sysData) setChecked(checked bool) { - c := uintptr(_BST_CHECKED) - if !checked { - c = uintptr(_BST_UNCHECKED) - } - _sendMessage.Call( - uintptr(s.hwnd), - uintptr(_BM_SETCHECK), - c, - uintptr(0)) -} -- cgit v1.2.3