X-Git-Url: https://code.delx.au/refind/blobdiff_plain/4944145c8979880d164195efcd9dae809c5bbf7e..76f5e60d25a0c4768bc6c3d5fb1344def1c0dcdc:/refind/menu.c diff --git a/refind/menu.c b/refind/menu.c index 55511be..08abe3e 100644 --- a/refind/menu.c +++ b/refind/menu.c @@ -34,13 +34,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012 Roderick W. Smith + * Modifications copyright (c) 2012-2017 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public - * License (GPL) version 3 (GPLv3), a copy of which must be distributed - * with this source code or binaries made from it. + * License (GPL) version 3 (GPLv3), or (at your option) any later version. * */ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "global.h" #include "screen.h" @@ -49,6 +62,8 @@ #include "config.h" #include "libeg.h" #include "libegint.h" +#include "line_edit.h" +#include "mystrings.h" #include "../include/refit_call_wrapper.h" #include "../include/egemb_back_selected_small.h" @@ -85,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 // @@ -334,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(); @@ -367,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; if (Screen->TimeoutSeconds > 0) { HaveTimeout = TRUE; @@ -437,52 +467,79 @@ static UINTN RunGenericMenu(IN REFIT_MENU_SCREEN *Screen, IN MENU_STYLE_FUNC Sty } } - // read key press (and wait for it if applicable) + // 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) { + + 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; break; } else if (HaveTimeout || GlobalConfig.ScreensaverTime > 0) { - EFI_EVENT TimerEvent; - UINTN ElapsCount = 1; + EFI_EVENT TimerEvent; + UINTN ElapsCount = 1; - Status = refit_call5_wrapper(BS->CreateEvent, EVT_TIMER, 0, NULL, NULL, &TimerEvent); - 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); - refit_call1_wrapper(BS->CloseEvent, TimerEvent); - if (EFI_ERROR(Status)) - refit_call1_wrapper(BS->Stall, 100000); // Pause for 100 ms - else if(Index == 0) - continue; - else - ElapsCount = 10; // always counted as 1s to end of the timeout - } - TimeSinceKeystroke += ElapsCount; - if(HaveTimeout) { - TimeoutCountdown = TimeoutCountdown <= ElapsCount ? 0 : TimeoutCountdown - ElapsCount; - } else if (GlobalConfig.ScreensaverTime > 0 && - TimeSinceKeystroke > (GlobalConfig.ScreensaverTime * 10)) - { - SaveScreen(); - State.PaintAll = TRUE; - TimeSinceKeystroke = 0; - } // if + Status = refit_call5_wrapper(BS->CreateEvent, EVT_TIMER, 0, NULL, NULL, &TimerEvent); + if (EFI_ERROR(Status)) { + refit_call1_wrapper(BS->Stall, 100000); // Pause for 100 ms + } else { + UINTN Index; + + refit_call3_wrapper(BS->SetTimer, TimerEvent, TimerRelative, 10000000); // 1s Timeout + + 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 || (TouchEnabled && Index == 1)) + continue; + else + ElapsCount = 10; // always counted as 1s to end of the timeout + } + TimeSinceKeystroke += ElapsCount; + if(HaveTimeout) { + TimeoutCountdown = TimeoutCountdown <= ElapsCount ? 0 : TimeoutCountdown - ElapsCount; + } else if (GlobalConfig.ScreensaverTime > 0 && + TimeSinceKeystroke > (GlobalConfig.ScreensaverTime * 10)) + { + SaveScreen(); + State.PaintAll = TRUE; + 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) { @@ -496,65 +553,92 @@ 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; + 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; + break; + case CHAR_BACKSPACE: + MenuExit = MENU_EXIT_ESCAPE; + break; + case '+': + case CHAR_TAB: + 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 + //the TouchProtocol min/max may not match the screen size + UINT32 TouchScreenPosX = (TouchState.CurrentX * UGAWidth) / TouchProtocol->Mode->AbsoluteMaxX; + UINT32 TouchScreenPosY = (TouchState.CurrentY * UGAHeight) / TouchProtocol->Mode->AbsoluteMaxY; + + + UINTN Item = FindMainMenuItem(Screen, &State, TouchScreenPosX, TouchScreenPosY); + 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; + } } } @@ -955,17 +1039,16 @@ static VOID DrawMainMenuEntry(REFIT_MENU_ENTRY *Entry, BOOLEAN selected, UINTN X { EG_IMAGE *Background; - if (SelectionImages != NULL) { - if (selected) { - Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, + // 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); + BltImageCompositeBadge(Background, Entry->Image, Entry->BadgeImage, XPos, YPos); + } else { // Image not selected; copy background + egDrawImageWithTransparency(Entry->Image, Entry->BadgeImage, XPos, YPos, SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height); - egComposeImage(Background, SelectionImages[Entry->Row], 0, 0); - BltImageCompositeBadge(Background, Entry->Image, Entry->BadgeImage, XPos, YPos); - } else { // Image not selected; copy background - egDrawImageWithTransparency(Entry->Image, Entry->BadgeImage, XPos, YPos, - SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height); - } // if/else - } // if + } // if/else } // VOID DrawMainMenuEntry() static VOID PaintAll(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *itemPosX, @@ -1052,7 +1135,7 @@ static VOID PaintIcon(IN EG_EMBEDDED_IMAGE *BuiltInIcon, IN CHAR16 *ExternalFile } } // static VOID () -inline UINTN ComputeRow0PosY(VOID) { +UINTN ComputeRow0PosY(VOID) { return ((UGAHeight / 2) - TileSizes[0] / 2); } // UINTN ComputeRow0PosY() @@ -1168,6 +1251,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. @@ -1231,6 +1391,13 @@ 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) {