From 2ede1b8a25d32e057bcb9d246a9fd855a09bfc7c Mon Sep 17 00:00:00 2001 From: CJ Vaughter Date: Tue, 3 May 2016 11:30:44 -0500 Subject: [PATCH] Added touchscreen support OS and tool selection via touch are supported, submenus only press enter on the currently selected item. --- refind/config.c | 3 + refind/global.h | 1 + refind/menu.c | 297 ++++++++++++++++++++++++++++++++++++------------ refind/menu.h | 5 + 4 files changed, 231 insertions(+), 75 deletions(-) diff --git a/refind/config.c b/refind/config.c index 658f8eb..195317e 100644 --- a/refind/config.c +++ b/refind/config.c @@ -761,6 +761,9 @@ VOID ReadConfig(CHAR16 *FileName) ReadConfig(TokenList[1]); } + } else if (MyStriCmp(TokenList[0], L"enable_touch")) { + GlobalConfig.EnableTouch = HandleBoolean(TokenList, TokenCount); + } FreeTokenLine(&TokenList, &TokenCount); diff --git a/refind/global.h b/refind/global.h index ad8e172..1b2a7f9 100644 --- a/refind/global.h +++ b/refind/global.h @@ -333,6 +333,7 @@ typedef struct { UINT32_LIST *CsrValues; UINTN ShowTools[NUM_TOOLS]; CHAR8 ScanFor[NUM_SCAN_OPTIONS]; // codes of types of loaders for which to scan + BOOLEAN EnableTouch; } REFIT_CONFIG; // Global variables diff --git a/refind/menu.c b/refind/menu.c index 4fa93b1..47f308c 100644 --- a/refind/menu.c +++ b/refind/menu.c @@ -100,6 +100,12 @@ static UINTN TileSizes[2] = { 144, 64 }; static EG_IMAGE *SelectionImages[2] = { NULL, NULL }; static EG_PIXEL SelectionBackgroundPixel = { 0xff, 0xff, 0xff, 0 }; +//Touch variables +EFI_ABSOLUTE_POINTER_PROTOCOL *TouchProtocol = NULL; +EFI_GUID TouchGuid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; +BOOLEAN TouchEnabled = FALSE; +BOOLEAN TouchActive = FALSE; + // // Graphics helper functions // @@ -349,16 +355,22 @@ static VOID IdentifyRows(IN SCROLL_STATE *State, IN REFIT_MENU_SCREEN *Screen) { State->MaxVisible = State->FinalRow0 + 1; } // static VOID IdentifyRows() -// Blank the screen, wait for a keypress, and restore banner/background. +// Blank the screen, wait for a keypress or touch event, and restore banner/background. // Screen may still require redrawing of text and icons on return. // TODO: Support more sophisticated screen savers, such as power-saving // mode and dynamic images. static VOID SaveScreen(VOID) { UINTN index; EG_PIXEL Black = { 0x0, 0x0, 0x0, 0 }; - + EFI_EVENT WaitList[2] = { ST->ConIn->WaitForKey, NULL }; + egClearScreen(&Black); - refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index); + if(TouchEnabled) { + WaitList[1] = TouchProtocol->WaitForInput; + refit_call3_wrapper(BS->WaitForEvent, 2, WaitList, &index); + } else { + refit_call3_wrapper(BS->WaitForEvent, 1, WaitList, &index); + } if (AllowGraphicsMode) SwitchToGraphicsAndClear(); ReadAllKeyStrokes(); @@ -382,6 +394,9 @@ static UINTN RunGenericMenu(IN REFIT_MENU_SCREEN *Screen, IN MENU_STYLE_FUNC Sty CHAR16 TimeoutMessage[256]; CHAR16 KeyAsString[2]; UINTN MenuExit; + + EFI_STATUS TouchStatus = EFI_NOT_READY; + EFI_ABSOLUTE_POINTER_STATE *TouchState = NULL; if (Screen->TimeoutSeconds > 0) { HaveTimeout = TRUE; @@ -452,9 +467,27 @@ static UINTN RunGenericMenu(IN REFIT_MENU_SCREEN *Screen, IN MENU_STYLE_FUNC Sty } } - // read key press (and wait for it if applicable) - Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key); - if (Status != EFI_SUCCESS) { + // read key press or touch event (and wait for them if applicable) + if(TouchEnabled) { + TouchStatus = refit_call2_wrapper(TouchProtocol->GetState, TouchProtocol, TouchState); + } + Status = refit_call2_wrapper(ST->ConIn->ReadKeyStroke, ST->ConIn, &key); + + if(Status == EFI_SUCCESS) { + TouchActive = FALSE; + TimeSinceKeystroke = 0; + } else if(TouchStatus == EFI_SUCCESS) { + if(StyleFunc != MainMenuStyle) { + // prevent user from getting stuck on submenus + // (the only one currently reachable without a keyboard is the about screen) + MenuExit = MENU_EXIT_ENTER; + break; + } + + TouchActive = TRUE; + TimeSinceKeystroke = 0; + } else { + EFI_EVENT WaitList[3] = { ST->ConIn->WaitForKey, NULL, NULL }; if (HaveTimeout && TimeoutCountdown == 0) { // timeout expired MenuExit = MENU_EXIT_TIMEOUT; @@ -467,17 +500,23 @@ static UINTN RunGenericMenu(IN REFIT_MENU_SCREEN *Screen, IN MENU_STYLE_FUNC Sty if (EFI_ERROR(Status)) { refit_call1_wrapper(BS->Stall, 100000); // Pause for 100 ms } else { - EFI_EVENT WaitList[2]; UINTN Index; refit_call3_wrapper(BS->SetTimer, TimerEvent, TimerRelative, 10000000); // 1s Timeout - WaitList[0] = ST->ConIn->WaitForKey; - WaitList[1] = TimerEvent; - Status = refit_call3_wrapper(BS->WaitForEvent, 2, WaitList, &Index); + + if(TouchEnabled) { + WaitList[1] = TouchProtocol->WaitForInput; + WaitList[2] = TimerEvent; + Status = refit_call3_wrapper(BS->WaitForEvent, 3, WaitList, &Index); + } else { + WaitList[1] = TimerEvent; + Status = refit_call3_wrapper(BS->WaitForEvent, 2, WaitList, &Index); + } + refit_call1_wrapper(BS->CloseEvent, TimerEvent); if (EFI_ERROR(Status)) refit_call1_wrapper(BS->Stall, 100000); // Pause for 100 ms - else if(Index == 0) + else if(Index == 0 || (TouchEnabled && Index == 1)) continue; else ElapsCount = 10; // always counted as 1s to end of the timeout @@ -493,11 +532,14 @@ static UINTN RunGenericMenu(IN REFIT_MENU_SCREEN *Screen, IN MENU_STYLE_FUNC Sty TimeSinceKeystroke = 0; } // if } else { - refit_call3_wrapper(BS->WaitForEvent, 1, &ST->ConIn->WaitForKey, &index); + if(TouchEnabled) { + WaitList[1] = TouchProtocol->WaitForInput; + refit_call3_wrapper(BS->WaitForEvent, 2, WaitList, &index); + } else { + refit_call3_wrapper(BS->WaitForEvent, 1, WaitList, &index); + } } continue; - } else { - TimeSinceKeystroke = 0; } // if/else !read keystroke if (HaveTimeout) { @@ -511,66 +553,85 @@ static UINTN RunGenericMenu(IN REFIT_MENU_SCREEN *Screen, IN MENU_STYLE_FUNC Sty } } - // react to key press - switch (key.ScanCode) { - case SCAN_UP: - UpdateScroll(&State, SCROLL_LINE_UP); - break; - case SCAN_LEFT: - UpdateScroll(&State, SCROLL_LINE_LEFT); - break; - case SCAN_DOWN: - UpdateScroll(&State, SCROLL_LINE_DOWN); - break; - case SCAN_RIGHT: - UpdateScroll(&State, SCROLL_LINE_RIGHT); - break; - case SCAN_HOME: - UpdateScroll(&State, SCROLL_FIRST); - break; - case SCAN_END: - UpdateScroll(&State, SCROLL_LAST); - break; - case SCAN_PAGE_UP: - UpdateScroll(&State, SCROLL_PAGE_UP); - break; - case SCAN_PAGE_DOWN: - UpdateScroll(&State, SCROLL_PAGE_DOWN); - break; - case SCAN_ESC: - MenuExit = MENU_EXIT_ESCAPE; - break; - case SCAN_INSERT: - case SCAN_F2: - MenuExit = MENU_EXIT_DETAILS; - break; - case SCAN_F10: - egScreenShot(); - break; - case 0x0016: // F12 - if (EjectMedia()) - MenuExit = MENU_EXIT_ESCAPE; - break; - } - switch (key.UnicodeChar) { - case CHAR_LINEFEED: - case CHAR_CARRIAGE_RETURN: - case ' ': - MenuExit = MENU_EXIT_ENTER; - break; - case '+': - MenuExit = MENU_EXIT_DETAILS; - break; - default: - KeyAsString[0] = key.UnicodeChar; - KeyAsString[1] = 0; - ShortcutEntry = FindMenuShortcutEntry(Screen, KeyAsString); - if (ShortcutEntry >= 0) { - State.CurrentSelection = ShortcutEntry; - MenuExit = MENU_EXIT_ENTER; - } - break; - } + if(!TouchActive) { // react to key press + switch (key.ScanCode) { + case SCAN_UP: + UpdateScroll(&State, SCROLL_LINE_UP); + break; + case SCAN_LEFT: + UpdateScroll(&State, SCROLL_LINE_LEFT); + break; + case SCAN_DOWN: + UpdateScroll(&State, SCROLL_LINE_DOWN); + break; + case SCAN_RIGHT: + UpdateScroll(&State, SCROLL_LINE_RIGHT); + break; + case SCAN_HOME: + UpdateScroll(&State, SCROLL_FIRST); + break; + case SCAN_END: + UpdateScroll(&State, SCROLL_LAST); + break; + case SCAN_PAGE_UP: + UpdateScroll(&State, SCROLL_PAGE_UP); + break; + case SCAN_PAGE_DOWN: + UpdateScroll(&State, SCROLL_PAGE_DOWN); + break; + case SCAN_ESC: + MenuExit = MENU_EXIT_ESCAPE; + break; + case SCAN_INSERT: + case SCAN_F2: + MenuExit = MENU_EXIT_DETAILS; + break; + case SCAN_F10: + egScreenShot(); + break; + case 0x0016: // F12 + if (EjectMedia()) + MenuExit = MENU_EXIT_ESCAPE; + break; + } + switch (key.UnicodeChar) { + case CHAR_LINEFEED: + case CHAR_CARRIAGE_RETURN: + case ' ': + MenuExit = MENU_EXIT_ENTER; + break; + case '+': + MenuExit = MENU_EXIT_DETAILS; + break; + default: + KeyAsString[0] = key.UnicodeChar; + KeyAsString[1] = 0; + ShortcutEntry = FindMenuShortcutEntry(Screen, KeyAsString); + if (ShortcutEntry >= 0) { + State.CurrentSelection = ShortcutEntry; + MenuExit = MENU_EXIT_ENTER; + } + break; + } + } else { //react to touch event + UINTN Item = FindMainMenuItem(Screen, &State, TouchState->CurrentX, TouchState->CurrentY); + + switch(Item) { + case TOUCH_NO_ITEM: + //do nothing + break; + case TOUCH_LEFT_ARROW: + UpdateScroll(&State, SCROLL_PAGE_UP); + break; + case TOUCH_RIGHT_ARROW: + UpdateScroll(&State, SCROLL_PAGE_DOWN); + break; + default: + State.CurrentSelection = Item; + MenuExit = MENU_EXIT_ENTER; + break; + } + } } StyleFunc(Screen, &State, MENU_FUNCTION_CLEANUP, NULL); @@ -970,7 +1031,8 @@ static VOID DrawMainMenuEntry(REFIT_MENU_ENTRY *Entry, BOOLEAN selected, UINTN X { EG_IMAGE *Background; - if (selected) { + // don't draw selection image if using touch + if (selected && !TouchActive) { Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height); egComposeImage(Background, SelectionImages[Entry->Row], 0, 0); @@ -1181,6 +1243,83 @@ VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINT } } // VOID MainMenuStyle() +// Determines the index of the main menu item at the given coordinates. +UINTN FindMainMenuItem(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINT64 PosX, IN UINT64 PosY) +{ + UINTN i; + static UINTN row0PosX, row0PosXRunning, row1PosY, row0Loaders; + UINTN row0Count, row1Count, row1PosX, row1PosXRunning; + static UINTN *itemPosX; + static UINTN row0PosY; + UINTN itemRow; + + row0Count = 0; + row1Count = 0; + row0Loaders = 0; + for (i = 0; i <= State->MaxIndex; i++) { + if (Screen->Entries[i]->Row == 1) { + row1Count++; + } else { + row0Loaders++; + if (row0Count < State->MaxVisible) + row0Count++; + } + } + row0PosX = (UGAWidth + TILE_XSPACING - (TileSizes[0] + TILE_XSPACING) * row0Count) >> 1; + row0PosY = ComputeRow0PosY(); + row1PosX = (UGAWidth + TILE_XSPACING - (TileSizes[1] + TILE_XSPACING) * row1Count) >> 1; + row1PosY = row0PosY + TileSizes[0] + TILE_YSPACING; + + if(PosY >= row0PosY && PosY <= row0PosY + TileSizes[0]) { + itemRow = 0; + if(PosX <= row0PosX) { + return TOUCH_LEFT_ARROW; + } + else if(PosX >= (UGAWidth - row0PosX)) { + return TOUCH_RIGHT_ARROW; + } + } else if(PosY >= row1PosY && PosY <= row1PosY + TileSizes[1]) { + itemRow = 1; + } else { // Y coordinate is outside of either row + return TOUCH_NO_ITEM; + } + + UINTN ItemIndex = TOUCH_NO_ITEM; + + itemPosX = AllocatePool(sizeof(UINTN) * Screen->EntryCount); + row0PosXRunning = row0PosX; + row1PosXRunning = row1PosX; + for (i = 0; i <= State->MaxIndex; i++) { + if (Screen->Entries[i]->Row == 0) { + itemPosX[i] = row0PosXRunning; + row0PosXRunning += TileSizes[0] + TILE_XSPACING; + } else { + itemPosX[i] = row1PosXRunning; + row1PosXRunning += TileSizes[1] + TILE_XSPACING; + } + } + + for (i = State->FirstVisible; i <= State->MaxIndex; i++) { + if (Screen->Entries[i]->Row == 0 && itemRow == 0) { + if (i <= State->LastVisible) { + if(PosX >= itemPosX[i - State->FirstVisible] && PosX <= itemPosX[i - State->FirstVisible] + TileSizes[0]) { + ItemIndex = i; + break; + } + } // if + } else if (Screen->Entries[i]->Row == 1 && itemRow == 1) { + if(PosX >= itemPosX[i] && PosX <= itemPosX[i] + TileSizes[1]) { + ItemIndex = i; + break; + } + } + } + + MyFreePool(itemPosX); + + return ItemIndex; +} // VOID FindMainMenuItem() + // Enable the user to edit boot loader options. // Returns TRUE if the user exited with edited options; FALSE if the user // pressed Esc to terminate the edit. @@ -1244,6 +1383,14 @@ UINTN RunMainMenu(REFIT_MENU_SCREEN *Screen, CHAR16** DefaultSelection, REFIT_ME if (AllowGraphicsMode) { Style = GraphicsMenuStyle; MainStyle = MainMenuStyle; + + if(GlobalConfig.EnableTouch) { + // Check for touch availability + EFI_STATUS status = refit_call3_wrapper(BS->LocateProtocol, &TouchGuid, NULL, (VOID **) &TouchProtocol); + if(status == EFI_SUCCESS) { + TouchEnabled = TouchActive = TRUE; + } + } } while (!MenuExit) { diff --git a/refind/menu.h b/refind/menu.h index 0b2db1f..1c76fd1 100644 --- a/refind/menu.h +++ b/refind/menu.h @@ -90,6 +90,10 @@ typedef struct { #define SCROLL_MODE_TEXT (0) /* Used in text mode & for GUI submenus */ #define SCROLL_MODE_ICONS (1) /* Used for main GUI menu */ +#define TOUCH_NO_ITEM (-1) +#define TOUCH_LEFT_ARROW (-2) +#define TOUCH_RIGHT_ARROW (-3) + struct _refit_menu_screen; VOID AddMenuInfoLine(IN REFIT_MENU_SCREEN *Screen, IN CHAR16 *InfoLine); @@ -98,6 +102,7 @@ UINTN ComputeRow0PosY(VOID); VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText); UINTN RunMenu(IN REFIT_MENU_SCREEN *Screen, OUT REFIT_MENU_ENTRY **ChosenEntry); UINTN RunMainMenu(IN REFIT_MENU_SCREEN *Screen, IN CHAR16** DefaultSelection, OUT REFIT_MENU_ENTRY **ChosenEntry); +UINTN FindMainMenuItem(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINT64 PosX, IN UINT64 PosY); #endif -- 2.39.2