Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions Application/ModernSetupApp/ModernSetupApp.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ UefiMain (
EFI_EVENT KeyEvent;
UINTN WaitCount;
UINTN WaitIndex;
UINTN PointerX;
UINTN PointerY;
BOOLEAN PointerVisible;
UINTN LastCursorX;
UINTN LastCursorY;
SETUP_PAGE TabHit;
UINTN CardHit;
UINTN ExitRowHit;
UINTN ExitOptionHit;
UINTN ListRowHit;
UINTN *ListSelPtr;
BOOLEAN ListRowWasActive;

gBS->SetWatchdogTimer (0, 0, 0, NULL);
mModernSetupImageHandle = ImageHandle;
Expand Down Expand Up @@ -136,11 +148,28 @@ UefiMain (
StatusMessage[0] = L'\0';
Redraw = TRUE;
ResetConfirmationPending = FALSE;
PointerX = 0;
PointerY = 0;
PointerVisible = FALSE;
LastCursorX = 0;
LastCursorY = 0;

for (;;) {
if (Redraw) {
Theme = ModernUiGetThemeForPreference (mModernSetupPreferences.ThemeId);
//
// The full repaint below invalidates any saved under-cursor pixels; the
// cursor is then re-composited (with a fresh capture) on top of the new
// frame.
//
ModernSetupInvalidatePointerCursor ();
ModernSetupDrawCurrentPage (&Ui, Theme, Page, Focus, DashboardSelection, BootSelection, DeviceSelection, PreferencesSelection, ExitSelection, StatusMessage);
if (PointerVisible) {
ModernSetupMovePointerCursor (&Ui, Theme, PointerX, PointerY);
LastCursorX = PointerX;
LastCursorY = PointerY;
}

Redraw = FALSE;
}

Expand Down Expand Up @@ -195,6 +224,114 @@ UefiMain (
CopyMem (&OldPreferences, &mModernSetupPreferences, sizeof (OldPreferences));
CopyMem (OldStatusMessage, StatusMessage, sizeof (OldStatusMessage));

if ((Event.Type == ModernUiInputPointer) && Event.PointerValid) {
//
// Scale the absolute pointer report into framebuffer pixels. When the
// device reports no usable range the raw values are taken as pixels.
//
PointerX = Event.PointerX;
PointerY = Event.PointerY;
if ((Input.Pointer != NULL) && (Input.Pointer->Mode != NULL) &&
(Input.Pointer->Mode->AbsoluteMaxX > Input.Pointer->Mode->AbsoluteMinX) &&
(Input.Pointer->Mode->AbsoluteMaxY > Input.Pointer->Mode->AbsoluteMinY) &&
(Ui.Width > 0) && (Ui.Height > 0))
{
PointerX = (UINTN)(((Event.PointerX - (UINTN)Input.Pointer->Mode->AbsoluteMinX) * (Ui.Width - 1)) /
(UINTN)(Input.Pointer->Mode->AbsoluteMaxX - Input.Pointer->Mode->AbsoluteMinX));
PointerY = (UINTN)(((Event.PointerY - (UINTN)Input.Pointer->Mode->AbsoluteMinY) * (Ui.Height - 1)) /
(UINTN)(Input.Pointer->Mode->AbsoluteMaxY - Input.Pointer->Mode->AbsoluteMinY));
}

PointerVisible = TRUE;

if (!Event.PointerPressed) {
//
// Motion only: composite the cursor with save-under (restore the old
// 16x16 rect, capture and draw at the new position). No full-frame
// repaint -- this is what keeps mouse motion flicker-free.
//
if ((PointerX != LastCursorX) || (PointerY != LastCursorY)) {
ModernSetupMovePointerCursor (&Ui, Theme, PointerX, PointerY);
LastCursorX = PointerX;
LastCursorY = PointerY;
}

continue;
}

//
// Click. Route through the same activation paths the keyboard uses:
// a successful hit updates the selection state and synthesizes an Enter
// event, so the shared Enter handling below stays the single owner of
// activation semantics.
//
if (ModernSetupHitTestTab (&Ui, Page, PointerX, PointerY, &TabHit)) {
Page = TabHit;
Focus = SetupFocusNav;
mModernSetupLanguageDropdownOpen = FALSE;
ModernSetupCancelPreferencePopup ();
StatusMessage[0] = L'\0';
Redraw = TRUE;
continue;
}

if ((Page == PageDashboard) && ModernSetupHitTestDashboardCard (&Ui, PointerX, PointerY, &CardHit)) {
DashboardSelection = CardHit;
Focus = SetupFocusContent;
Event.Type = ModernUiInputEnter;
} else if ((Page == PageExit) && ModernSetupHitTestExitRow (&Ui, PointerX, PointerY, &ExitRowHit, &ExitOptionHit)) {
if (ExitOptionHit != (UINTN)-1) {
mModernSetupLanguageDropdownSelection = ExitOptionHit;
} else {
ExitSelection = ExitRowHit;
}

Focus = SetupFocusContent;
Event.Type = ModernUiInputEnter;
} else if (ModernSetupHitTestPageListRow (&Ui, Page, PointerX, PointerY, &ListRowHit)) {
//
// Boot / Devices / Preferences list rows are two-stage: the first click
// only selects the row (focus + highlight), and a second click on the
// already-selected row activates it (launch boot option, open the
// native HII form, or open the preference popup) -- so a stray click
// never launches anything. Activation reuses the shared Enter handling.
//
ListSelPtr = NULL;
switch (Page) {
case PageBoot:
ListSelPtr = &BootSelection;
break;
case PageDevices:
ListSelPtr = &DeviceSelection;
break;
case PagePreferences:
ListSelPtr = &PreferencesSelection;
break;
default:
break;
}

if (ListSelPtr != NULL) {
ListRowWasActive = (BOOLEAN)((Focus == SetupFocusContent) && (*ListSelPtr == ListRowHit));
*ListSelPtr = ListRowHit;
Focus = SetupFocusContent;
if (ListRowWasActive) {
Event.Type = ModernUiInputEnter;
} else {
StatusMessage[0] = L'\0';
Redraw = TRUE;
continue;
}
} else {
Redraw = TRUE;
continue;
}
} else {
Redraw = TRUE;
continue;
}
}

if ((Focus == SetupFocusContent) && (Page == PagePreferences) && mModernSetupPreferencePopupOpen && (Event.Type == ModernUiInputOther)) {
ModernSetupHandlePreferenceInputKey (&Event, StatusMessage, sizeof (StatusMessage));
ResetConfirmationPending = FALSE;
Expand Down
205 changes: 205 additions & 0 deletions Application/ModernSetupApp/ModernSetupAppActions.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,137 @@ ModernSetupDashboardVisibleQuickCardCount (
return MAX (Count, (UINTN)1);
}

/**
Hit-test the dashboard quick-card grid for a pointer click. See
ModernSetupAppInternal.h.

@param[in] Ui Initialized render context. Must not be NULL.
@param[in] X Pointer X in pixels.
@param[in] Y Pointer Y in pixels.
@param[out] Card Receives the catalog index of the clicked card.

@retval TRUE (X,Y) lies on a visible quick card; *Card is set.
@retval FALSE No card at this position.
**/
BOOLEAN
ModernSetupHitTestDashboardCard (
IN MODERN_UI_RENDER_CONTEXT *Ui,
IN UINTN X,
IN UINTN Y,
OUT UINTN *Card
)
{
MODERN_SETUP_DASHBOARD_QUICK_GRID Grid;
UINTN VisibleCount;
UINTN Index;
UINTN CardX;
UINTN CardY;

if ((Ui == NULL) || (Card == NULL)) {
return FALSE;
}

if (!ModernSetupGetDashboardQuickGrid (Ui, mModernSetupPreferences.DashboardDensity, &Grid) ||
!Grid.Visible || (Grid.CardsPerRow == 0))
{
return FALSE;
}

//
// Same placement formula the dashboard drawing loop uses; bounded by the
// platform-visible count so a hidden card can never be clicked.
//
VisibleCount = ModernSetupDashboardVisibleQuickCardCount ();
for (Index = 0; Index < VisibleCount; Index++) {
CardX = Grid.Panel.X + 20 + ((Index % Grid.CardsPerRow) * (Grid.CardWidth + Grid.CardGap));
CardY = Grid.Panel.Y + Grid.CardTop + ((Index / Grid.CardsPerRow) * (Grid.CardHeight + Grid.CardGap));
if ((X >= CardX) && (X < (CardX + Grid.CardWidth)) &&
(Y >= CardY) && (Y < (CardY + Grid.CardHeight)))
{
*Card = Index;
return TRUE;
}
}

return FALSE;
}

/**
Hit-test the Exit page rows and the open language dropdown. See
ModernSetupAppInternal.h.

@param[in] Ui Initialized render context. Must not be NULL.
@param[in] X Pointer X in pixels.
@param[in] Y Pointer Y in pixels.
@param[out] Row Receives the clicked row index.
@param[out] DropdownOption Receives the clicked dropdown option, or
(UINTN)-1 when the click is on a row.

@retval TRUE (X,Y) lies on an Exit row or an open dropdown option.
@retval FALSE Nothing clickable at this position.
**/
BOOLEAN
ModernSetupHitTestExitRow (
IN MODERN_UI_RENDER_CONTEXT *Ui,
IN UINTN X,
IN UINTN Y,
OUT UINTN *Row,
OUT UINTN *DropdownOption
)
{
MODERN_UI_RECT Panel;
UINTN RowX;
UINTN RowWidth;
UINTN Index;
UINTN RowTop;
UINTN DropdownX;
UINTN DropdownY;
UINTN Option;
UINTN OptionTop;

if ((Ui == NULL) || (Row == NULL) || (DropdownOption == NULL)) {
return FALSE;
}

Panel = ModernSetupContentRect (Ui);
RowX = Panel.X + 26;
RowWidth = (Panel.Width > 52) ? (Panel.Width - 52) : Panel.Width;

//
// The open dropdown floats above the rows, so test it first. Geometry mirrors
// DrawExit's dropdown block.
//
if (mModernSetupLanguageDropdownOpen) {
DropdownX = RowX + RowWidth - MODERN_SETUP_EXIT_VALUE_WIDTH - 12;
DropdownY = Panel.Y + MODERN_SETUP_EXIT_ROW_TOP + MODERN_SETUP_EXIT_ROW_COUNT * MODERN_SETUP_EXIT_ROW_STRIDE - 8;
for (Option = 0; Option < 2; Option++) {
OptionTop = DropdownY + 7 + Option * 34;
if ((X >= (DropdownX + 6)) && (X < (DropdownX + MODERN_SETUP_EXIT_VALUE_WIDTH - 6)) &&
(Y >= OptionTop) && (Y < (OptionTop + 30)))
{
*Row = MODERN_SETUP_EXIT_ROW_COUNT - 1;
*DropdownOption = Option;
return TRUE;
}
}
}

if ((X < RowX) || (X >= (RowX + RowWidth))) {
return FALSE;
}

for (Index = 0; Index < MODERN_SETUP_EXIT_ROW_COUNT; Index++) {
RowTop = Panel.Y + MODERN_SETUP_EXIT_ROW_TOP + Index * MODERN_SETUP_EXIT_ROW_STRIDE - 10;
if ((Y >= RowTop) && (Y < (RowTop + MODERN_SETUP_EXIT_ROW_HEIGHT))) {
*Row = Index;
*DropdownOption = (UINTN)-1;
return TRUE;
}
}

return FALSE;
}

BOOLEAN mModernSetupLanguageDropdownOpen;
UINTN mModernSetupLanguageDropdownSelection;
BOOLEAN mModernSetupPreferencePopupOpen;
Expand Down Expand Up @@ -724,6 +855,80 @@ ModernSetupGetPageSelectableCount (
}
}

/**
Hit-test a list-page (Boot / Devices / Preferences) row for a pointer click.
See ModernSetupAppInternal.h.

@param[in] Ui Initialized render context. Must not be NULL.
@param[in] Page List page under test.
@param[in] X Pointer X in pixels.
@param[in] Y Pointer Y in pixels.
@param[out] Row Receives the clicked visible row index. Must not be NULL.

@retval TRUE (X,Y) lies on a visible list row; *Row is set.
@retval FALSE Not a list page, or no row at this position.
**/
BOOLEAN
ModernSetupHitTestPageListRow (
IN MODERN_UI_RENDER_CONTEXT *Ui,
IN SETUP_PAGE Page,
IN UINTN X,
IN UINTN Y,
OUT UINTN *Row
)
{
MODERN_SETUP_PAGE_LIST_LAYOUT Layout;
UINTN HardRowCap;
BOOLEAN AllowPreviewPane;
UINTN VisibleCount;
UINTN Index;

if ((Ui == NULL) || (Row == NULL)) {
return FALSE;
}

//
// Same layout parameters the page's drawing and selectable-count use, so the
// click bands match the painted rows exactly.
//
switch (Page) {
case PageBoot:
HardRowCap = MAX_BOOT_ROWS + MODERN_SETUP_NATIVE_BOOT_TOOLS_ROW_COUNT;
AllowPreviewPane = FALSE;
break;
case PageDevices:
HardRowCap = MAX_DEVICE_ROWS;
AllowPreviewPane = TRUE;
break;
case PagePreferences:
HardRowCap = MODERN_SETUP_PREFERENCE_ROW_COUNT;
AllowPreviewPane = FALSE;
break;
default:
return FALSE;
}

if (!ModernSetupGetPageListLayout (Ui, mModernSetupPreferences.DashboardDensity, HardRowCap, AllowPreviewPane, &Layout)) {
return FALSE;
}

VisibleCount = ModernSetupGetPageSelectableCount (Ui, Page);
if ((VisibleCount == 0) || (Layout.RowStride == 0) ||
(X < Layout.RowX) || (X >= (Layout.RowX + Layout.RowWidth)) ||
(Y < Layout.FirstRowY))
{
return FALSE;
}

Index = (Y - Layout.FirstRowY) / Layout.RowStride;
if (Index >= VisibleCount) {
return FALSE;
}

*Row = Index;
return TRUE;
}

/**
Launch the selected visible Boot#### option.

Expand Down
Loading
Loading