BootPicker.c 58.5 KB
Newer Older
1
/** @file
V
vit9696 已提交
2
  This file is part of OpenCanopy, OpenCore GUI.
3 4 5 6 7 8 9

  Copyright (c) 2018-2019, Download-Fritz. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-3-Clause
**/

#include <Uefi.h>

10 11
#include <IndustryStandard/AppleIcon.h>

12
#include <Library/UefiBootServicesTableLib.h>
13
#include <Library/UefiRuntimeServicesTableLib.h>
14 15 16 17 18

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
V
vit9696 已提交
19 20 21
#include <Library/OcBootManagementLib.h>
#include <Library/OcPngLib.h>
#include <Library/OcStorageLib.h>
22
#include <Library/OcMiscLib.h>
23

V
vit9696 已提交
24
#include "../OpenCanopy.h"
25 26
#include "../BmfLib.h"
#include "../GuiApp.h"
27
#include "../GuiIo.h"
T
TaiPhamD 已提交
28
#include "BootPicker.h"
29

30
#include "Common.h"
31
#include "../Blending.h"
32

33 34 35
#define BOOT_LABEL_WRAPAROUND_PADDING   30U
#define BOOT_LABEL_SCROLLING_HOLD_TIME  180U

36 37 38
#define BOOT_LABEL_SHADOW_WIDTH   8U
#define BOOT_LABEL_SHADOW_HEIGHT  BOOT_ENTRY_LABEL_HEIGHT

39
extern GUI_KEY_CONTEXT   *mKeyContext;
40 41

extern GUI_VOLUME_PICKER mBootPicker;
42
extern GUI_OBJ_CHILD     mBootPickerContainer;
43 44 45
extern GUI_OBJ_CHILD     mBootPickerSelectorContainer;
extern GUI_OBJ_CHILD     mBootPickerSelectorBackground;
extern GUI_OBJ_CLICKABLE mBootPickerSelectorButton;
46 47
extern GUI_OBJ_CLICKABLE mBootPickerRightScroll;
extern GUI_OBJ_CLICKABLE mBootPickerLeftScroll;
48
extern CONST GUI_IMAGE   mBackgroundImage;
49

50
// STATIC UINT8 mBootPickerImageIndex = 0;
51

52 53 54
extern INT64 mBackgroundImageOffsetX;
extern INT64 mBackgroundImageOffsetY;

55 56
STATIC UINT32 mBootPickerLabelScrollHoldTime = 0;

57 58 59 60 61
STATIC GUI_OBJ *mBootPickerFocusList[] = {
  &mBootPicker.Hdr.Obj,
  &mCommonRestart.Hdr.Obj,
  &mCommonShutDown.Hdr.Obj
};
62

M
MikeBeaton 已提交
63 64 65 66
STATIC GUI_OBJ *mBootPickerFocusListMinimal[] = {
  &mBootPicker.Hdr.Obj
};

67 68 69 70 71 72 73 74 75 76 77 78
STATIC
GUI_VOLUME_ENTRY *
InternalGetVolumeEntry (
  IN UINT32  Index
  )
{
  ASSERT (Index < mBootPicker.Hdr.Obj.NumChildren);
  return (GUI_VOLUME_ENTRY *) (
    mBootPicker.Hdr.Obj.Children[Index]
    );
}

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
STATIC
VOID
InternalRedrawVolumeLabel (
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     CONST GUI_VOLUME_ENTRY  *Entry
  )
{
  GuiRequestDrawCrop (
    DrawContext,
    mBootPickerContainer.Obj.OffsetX + mBootPicker.Hdr.Obj.OffsetX + Entry->Hdr.Obj.OffsetX,
    (mBootPickerContainer.Obj.OffsetY + mBootPicker.Hdr.Obj.OffsetY + Entry->Hdr.Obj.OffsetY + Entry->Hdr.Obj.Height - Entry->Label.Height),
    Entry->Hdr.Obj.Width,
    Entry->Label.Height
    );
}

BOOLEAN
InternalBootPickerAnimateLabel (
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     UINT64                  CurrentTime
  )
{
  GUI_VOLUME_ENTRY *Entry;

  ASSERT (DrawContext != NULL);

  if (mBootPickerLabelScrollHoldTime < BOOT_LABEL_SCROLLING_HOLD_TIME) {
    ++mBootPickerLabelScrollHoldTime;
    return FALSE;
  }

  Entry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
  if (Entry->Label.Width <= Entry->Hdr.Obj.Width) {
    return FALSE;
  }

116
  Entry->ShowLeftShadow = TRUE;
117
  Entry->LabelOffset -= DrawContext->Scale;
118 119 120 121 122 123 124 125 126 127 128 129 130

  if (Entry->LabelOffset <= -(INT16) Entry->Label.Width) {
    //
    // Hide the left shadow once the first label is hidden.
    //
    Entry->ShowLeftShadow = FALSE;
    //
    // If the second drawn label reaches the front, switch back to the first.
    //
    if (Entry->LabelOffset <= -(INT16) (Entry->Label.Width + BOOT_LABEL_WRAPAROUND_PADDING * DrawContext->Scale)) {
      Entry->LabelOffset = 0;
      mBootPickerLabelScrollHoldTime = 0;
    }
131 132 133 134 135 136 137 138 139 140 141 142 143
  }

  InternalRedrawVolumeLabel (DrawContext, Entry);

  return FALSE;
}

STATIC GUI_ANIMATION mBootPickerLabelAnimation = {
  INITIALIZE_LIST_HEAD_VARIABLE (mBootPickerLabelAnimation.Link),
  NULL,
  InternalBootPickerAnimateLabel
};

144
VOID
145 146 147
InternalStartAnimateLabel (
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     CONST GUI_VOLUME_ENTRY  *Entry
148 149
  )
{
150 151 152 153 154
  ASSERT (!IsNodeInList (&DrawContext->Animations, &mBootPickerLabelAnimation.Link));
  //
  // Reset the boot entry label scrolling timer.
  //
  mBootPickerLabelScrollHoldTime = 0;
155

156
  if (Entry->Label.Width > Entry->Hdr.Obj.Width) {
157 158 159 160 161 162 163
    //
    // Add the animation if the next entry needs scrolling.
    //
    InsertHeadList (&DrawContext->Animations, &mBootPickerLabelAnimation.Link);
  }
}

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
VOID
InternalStopAnimateLabel (
  IN OUT GUI_DRAWING_CONTEXT  *DrawContext,
  IN OUT GUI_VOLUME_ENTRY     *Entry
  )
{
  if (Entry->Label.Width > Entry->Hdr.Obj.Width) {
    //
    // Reset the label of the old entry back to its default position.
    //
    if (Entry->LabelOffset != 0) {
      Entry->ShowLeftShadow = FALSE;
      Entry->LabelOffset    = 0;
      InternalRedrawVolumeLabel (DrawContext, Entry);
    }

    RemoveEntryList (&mBootPickerLabelAnimation.Link);
    InitializeListHead (&mBootPickerLabelAnimation.Link);
  }
}

185 186
VOID
InternalBootPickerSelectEntry (
187 188
  IN OUT GUI_VOLUME_PICKER   *This,
  IN OUT GUI_DRAWING_CONTEXT *DrawContext,
189
  IN     UINT32              NewIndex
190 191
  )
{
192
  GUI_VOLUME_ENTRY       *OldEntry;
193
  CONST GUI_VOLUME_ENTRY *NewEntry;
194 195

  ASSERT (This != NULL);
196
  ASSERT (NewIndex < mBootPicker.Hdr.Obj.NumChildren);
197

198
  OldEntry = InternalGetVolumeEntry (This->SelectedIndex);
199
  This->SelectedIndex = NewIndex;
200
  NewEntry = InternalGetVolumeEntry (NewIndex);
201

202
  ASSERT (NewEntry->Hdr.Obj.Width <= mBootPickerSelectorContainer.Obj.Width);
203
  ASSERT_EQUALS (This->Hdr.Obj.Height, mBootPickerSelectorContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.Height);
204

205
  mBootPickerSelectorContainer.Obj.OffsetX = mBootPicker.Hdr.Obj.OffsetX + NewEntry->Hdr.Obj.OffsetX - (mBootPickerSelectorContainer.Obj.Width - NewEntry->Hdr.Obj.Width) / 2;
206 207

  if (DrawContext != NULL) {
208
    if (OldEntry->Label.Width > OldEntry->Hdr.Obj.Width) {
209
      ASSERT (IsNodeInList (&DrawContext->Animations, &mBootPickerLabelAnimation.Link));
210
    }
211 212 213

    InternalStopAnimateLabel (DrawContext, OldEntry);
    InternalStartAnimateLabel (DrawContext, NewEntry);
214 215 216 217
    //
    // Set voice timeout to N frames from now.
    //
    DrawContext->GuiContext->AudioPlaybackTimeout = OC_VOICE_OVER_IDLE_TIMEOUT_MS;
218
    DrawContext->GuiContext->VoAction = CanopyVoSelectedEntry;
219
    DrawContext->GuiContext->BootEntry = NewEntry->Context;
220
  }
221 222
}

223 224
INT64
InternelBootPickerScrollSelected (
225
  VOID
226 227 228 229
  )
{
  CONST GUI_VOLUME_ENTRY *SelectedEntry;
  INT64                  EntryOffsetX;
230
  UINT32                 EntryWidth;
231

232
  if (mBootPicker.Hdr.Obj.NumChildren == 1) {
233 234
    return 0;
  }
235 236 237
  //
  // If the selected entry is outside of the view, scroll it accordingly.
  //
238
  SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
239 240
  EntryOffsetX  = mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX - (mBootPickerSelectorContainer.Obj.Width - SelectedEntry->Hdr.Obj.Width) / 2;
  EntryWidth    = SelectedEntry->Hdr.Obj.Width + (mBootPickerSelectorContainer.Obj.Width - SelectedEntry->Hdr.Obj.Width);
241 242

  if (EntryOffsetX < 0) {
243
    return -EntryOffsetX;
244 245
  }
  
246 247
  if (EntryOffsetX + EntryWidth > mBootPickerContainer.Obj.Width) {
    return -((EntryOffsetX + EntryWidth) - mBootPickerContainer.Obj.Width);
248 249 250 251 252
  }

  return 0;
}

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
VOID
InternalUpdateScrollButtons (
  VOID
  )
{
  if (mBootPicker.Hdr.Obj.OffsetX < 0) {
    mBootPickerLeftScroll.Hdr.Obj.Opacity = 0xFF;
  } else {
    mBootPickerLeftScroll.Hdr.Obj.Opacity = 0;
  }

  if (mBootPicker.Hdr.Obj.OffsetX + mBootPicker.Hdr.Obj.Width > mBootPickerContainer.Obj.Width) {
    mBootPickerRightScroll.Hdr.Obj.Opacity = 0xFF;
  } else {
    mBootPickerRightScroll.Hdr.Obj.Opacity = 0;
  }
}

271 272 273 274 275 276 277 278 279 280 281 282 283
VOID
InternalBootPickerScroll (
  IN OUT GUI_VOLUME_PICKER    *This,
  IN OUT GUI_DRAWING_CONTEXT  *DrawContext,
  IN     INT64                BaseX,
  IN     INT64                BaseY,
  IN     INT64                ScrollOffset
  )
{
  INT64 ScrollX;
  INT64 ScrollY;

  mBootPicker.Hdr.Obj.OffsetX += ScrollOffset;
284 285
  mBootPickerSelectorContainer.Obj.OffsetX += ScrollOffset;
  InternalUpdateScrollButtons ();
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
  //
  // The entry list has been scrolled, the entire horizontal space to also cover
  // the scroll buttons.
  //
  DEBUG_CODE_BEGIN ();
  GuiGetBaseCoords (
    &mBootPickerLeftScroll.Hdr.Obj,
    DrawContext,
    &ScrollX,
    &ScrollY
    );
  ASSERT (ScrollY >= BaseY);
  ASSERT (ScrollY + mBootPickerLeftScroll.Hdr.Obj.Height <= BaseY + This->Hdr.Obj.Height);

  GuiGetBaseCoords (
    &mBootPickerRightScroll.Hdr.Obj,
    DrawContext,
    &ScrollX,
    &ScrollY
    );
  ASSERT (ScrollY >= BaseY);
  ASSERT (ScrollY + mBootPickerRightScroll.Hdr.Obj.Height <= BaseY + This->Hdr.Obj.Height);
  DEBUG_CODE_END ();
309 310 311 312
  //
  // The container is constructed such that it is always fully visible.
  //
  ASSERT (This->Hdr.Obj.Height <= mBootPickerContainer.Obj.Height);
313
  ASSERT (BaseY + This->Hdr.Obj.Height <= DrawContext->Screen.Height);
314
  
315
  GuiRequestDraw (
316
    0,
317
    (UINT32) BaseY,
318
    DrawContext->Screen.Width,
319
    This->Hdr.Obj.Height
320 321 322
    );
}

323 324 325 326 327 328
VOID
InternalBootPickerChangeEntry (
  IN OUT GUI_VOLUME_PICKER    *This,
  IN OUT GUI_DRAWING_CONTEXT  *DrawContext,
  IN     INT64                BaseX,
  IN     INT64                BaseY,
329
  IN     UINT32               NewIndex
330 331
  )
{
332 333
  INT64 ScrollOffset;
  INT64 OldSelectorOffsetX;
334 335 336

  ASSERT (This != NULL);
  ASSERT (DrawContext != NULL);
337
  ASSERT (NewIndex < This->Hdr.Obj.NumChildren);
338 339 340 341
  //
  // The caller must guarantee the entry is actually new for performance
  // reasons.
  //
342
  ASSERT (This->SelectedIndex != NewIndex);
343 344

  OldSelectorOffsetX = mBootPickerSelectorContainer.Obj.OffsetX;
345
  InternalBootPickerSelectEntry (This, DrawContext, NewIndex);
346

347
  ScrollOffset = InternelBootPickerScrollSelected ();
348
  if (ScrollOffset == 0) {
349
    GuiRequestDrawCrop (
350
      DrawContext,
351 352 353 354
      mBootPickerContainer.Obj.OffsetX + OldSelectorOffsetX,
      mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY,
      mBootPickerSelectorContainer.Obj.Width,
      mBootPickerSelectorContainer.Obj.Height
355
      );
356

357
    GuiRequestDrawCrop (
358
      DrawContext,
359 360 361 362
      mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX,
      mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY,
      mBootPickerSelectorContainer.Obj.Width,
      mBootPickerSelectorContainer.Obj.Height
363 364
      );
  } else {
365 366
    InternalBootPickerScroll (
      This,
367
      DrawContext,
368
      BaseX,
369
      BaseY,
370
      ScrollOffset
371 372
      );
  }
373 374 375 376
}

VOID
InternalBootPickerKeyEvent (
377 378 379
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *GuiContext,
380
  IN     CONST GUI_KEY_EVENT     *KeyEvent
381 382 383
  )
{
  GUI_VOLUME_PICKER       *Picker;
384 385
  INT64                   BaseX;
  INT64                   BaseY;
386
  CONST GUI_VOLUME_ENTRY  *SelectedEntry;
387
  UINT8                   ImageId;
388 389

  ASSERT (This != NULL);
390
  ASSERT (GuiContext != NULL);
391 392
  ASSERT (DrawContext != NULL);

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
  if ((KeyEvent->OcModifiers & OC_MODIFIERS_SET_DEFAULT) == 0) {
    ImageId = ICON_SELECTOR;
  } else {
    ImageId = ICON_SET_DEFAULT;
  }

  if (mBootPickerSelectorButton.ImageId != ImageId) {
    mBootPickerSelectorButton.ImageId = ImageId;
    GuiRequestDraw (
      (UINT32) (mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX + mBootPickerSelectorButton.Hdr.Obj.OffsetX),
      (UINT32) (mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY + mBootPickerSelectorButton.Hdr.Obj.OffsetY),
      mBootPickerSelectorButton.Hdr.Obj.Width,
      mBootPickerSelectorButton.Hdr.Obj.Height
      );
  }

409
  Picker = BASE_CR (This, GUI_VOLUME_PICKER, Hdr.Obj);
410

411 412 413 414
  BaseX = mBootPickerContainer.Obj.OffsetX + mBootPicker.Hdr.Obj.OffsetX;
  BaseY = mBootPickerContainer.Obj.OffsetY + mBootPicker.Hdr.Obj.OffsetY;

  if (KeyEvent->OcKeyCode == OC_INPUT_RIGHT) {
415 416 417 418 419 420 421 422 423 424 425
    if (mBootPicker.Hdr.Obj.NumChildren > 1) {
      InternalBootPickerChangeEntry (
        Picker,
        DrawContext,
        BaseX,
        BaseY,
        mBootPicker.SelectedIndex + 1 < mBootPicker.Hdr.Obj.NumChildren
          ? mBootPicker.SelectedIndex + 1
          : 0
        );
    }
426
  } else if (KeyEvent->OcKeyCode == OC_INPUT_LEFT) {
427
    ASSERT (mBootPicker.Hdr.Obj.NumChildren > 0);
428 429 430 431 432 433 434 435 436 437 438
    if (mBootPicker.Hdr.Obj.NumChildren > 1) {
      InternalBootPickerChangeEntry (
        Picker,
        DrawContext,
        BaseX,
        BaseY,
        mBootPicker.SelectedIndex > 0
          ? mBootPicker.SelectedIndex - 1
          : mBootPicker.Hdr.Obj.NumChildren - 1
        );
    }
439
  } else if (KeyEvent->OcKeyCode == OC_INPUT_CONTINUE) {
440
    if (mBootPicker.Hdr.Obj.NumChildren > 0) {
441
      SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
442
      SelectedEntry->Context->SetDefault = (KeyEvent->OcModifiers & OC_MODIFIERS_SET_DEFAULT) != 0;
443 444 445
      GuiContext->ReadyToBoot = TRUE;
      ASSERT (GuiContext->BootEntry == SelectedEntry->Context);
    }
446
  } else if (mBootPickerContainer.Obj.Opacity != 0xFF) {
447 448 449 450 451 452
    //
    // FIXME: Other keys are not allowed when boot picker is partially transparent.
    //
    return;
  }

453
  if (KeyEvent->OcKeyCode == OC_INPUT_MORE) {
454 455 456 457 458 459 460 461 462 463 464 465
    //
    // Match Builtin picker logic here: only refresh if the keypress makes a change
    //
    if (GuiContext->HideAuxiliary) {
      GuiContext->HideAuxiliary = FALSE;
      GuiContext->Refresh = TRUE;
      DrawContext->GuiContext->PickerContext->PlayAudioFile (
        DrawContext->GuiContext->PickerContext,
        OcVoiceOverAudioFileShowAuxiliary,
        FALSE
        );
    }
466
  } else if (KeyEvent->OcKeyCode == OC_INPUT_ABORTED) {
467
    GuiContext->Refresh = TRUE;
468 469 470 471 472
    DrawContext->GuiContext->PickerContext->PlayAudioFile (
      DrawContext->GuiContext->PickerContext,
      OcVoiceOverAudioFileReloading,
      FALSE
      );
473 474 475
  }
}

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
STATIC EFI_GRAPHICS_OUTPUT_BLT_PIXEL mBootPickerLabelLeftShadowBuffer[2 * BOOT_LABEL_SHADOW_WIDTH * 2 * BOOT_LABEL_SHADOW_HEIGHT];
STATIC GUI_IMAGE mBootPickerLabelLeftShadowImage;

STATIC EFI_GRAPHICS_OUTPUT_BLT_PIXEL mBootPickerLabelRightShadowBuffer[2 * BOOT_LABEL_SHADOW_WIDTH * 2 * BOOT_LABEL_SHADOW_HEIGHT];
STATIC GUI_IMAGE mBootPickerLabelRightShadowImage;

VOID
InternalInitialiseLabelShadows (
  IN CONST GUI_DRAWING_CONTEXT  *DrawContext,
  IN BOOT_PICKER_GUI_CONTEXT    *GuiContext
  )
{
  UINT32 RowOffset;
  UINT32 ColumnOffset;
  UINT32 MaxOffset;
  UINT8  Opacity;

  EFI_GRAPHICS_OUTPUT_BLT_PIXEL LeftPixel;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL RightPixel;

  ASSERT (DrawContext->Scale <= 2);

  mBootPickerLabelLeftShadowImage.Width  = BOOT_LABEL_SHADOW_WIDTH * DrawContext->Scale;
  mBootPickerLabelLeftShadowImage.Height = BOOT_LABEL_SHADOW_HEIGHT * DrawContext->Scale;
  mBootPickerLabelLeftShadowImage.Buffer = mBootPickerLabelLeftShadowBuffer;

  mBootPickerLabelRightShadowImage.Width  = BOOT_LABEL_SHADOW_WIDTH * DrawContext->Scale;
  mBootPickerLabelRightShadowImage.Height = BOOT_LABEL_SHADOW_HEIGHT * DrawContext->Scale;
  mBootPickerLabelRightShadowImage.Buffer = mBootPickerLabelRightShadowBuffer;

  MaxOffset = mBootPickerLabelLeftShadowImage.Width * mBootPickerLabelLeftShadowImage.Height;

  for (
    ColumnOffset = 0;
    ColumnOffset < mBootPickerLabelLeftShadowImage.Width;
    ++ColumnOffset
    ) {
    Opacity = (UINT8) (((ColumnOffset + 1) * 0xFF) / (mBootPickerLabelLeftShadowImage.Width + 1));

    LeftPixel.Blue = RGB_APPLY_OPACITY (
      GuiContext->BackgroundColor.Pixel.Blue,
      0xFF - Opacity
      );
    LeftPixel.Green = RGB_APPLY_OPACITY (
      GuiContext->BackgroundColor.Pixel.Green,
      0xFF - Opacity
      );
    LeftPixel.Red = RGB_APPLY_OPACITY (
      GuiContext->BackgroundColor.Pixel.Red,
      0xFF - Opacity
      );
    LeftPixel.Reserved = 0xFF - Opacity;

    RightPixel.Blue =
      RGB_APPLY_OPACITY (
        GuiContext->BackgroundColor.Pixel.Blue,
        Opacity
        );
    RightPixel.Green =
      RGB_APPLY_OPACITY (
        GuiContext->BackgroundColor.Pixel.Green,
        Opacity
        );
    RightPixel.Red =
      RGB_APPLY_OPACITY (
        GuiContext->BackgroundColor.Pixel.Red,
        Opacity
        );
    RightPixel.Reserved = Opacity;

    for (
      RowOffset = 0;
      RowOffset < MaxOffset;
      RowOffset += mBootPickerLabelLeftShadowImage.Width
      ) {
      mBootPickerLabelLeftShadowBuffer[RowOffset + ColumnOffset].Blue     = LeftPixel.Blue;
      mBootPickerLabelLeftShadowBuffer[RowOffset + ColumnOffset].Green    = LeftPixel.Green;
      mBootPickerLabelLeftShadowBuffer[RowOffset + ColumnOffset].Red      = LeftPixel.Red;
      mBootPickerLabelLeftShadowBuffer[RowOffset + ColumnOffset].Reserved = LeftPixel.Reserved;

      mBootPickerLabelRightShadowBuffer[RowOffset + ColumnOffset].Blue     = RightPixel.Blue;
      mBootPickerLabelRightShadowBuffer[RowOffset + ColumnOffset].Green    = RightPixel.Green;
      mBootPickerLabelRightShadowBuffer[RowOffset + ColumnOffset].Red      = RightPixel.Red;
      mBootPickerLabelRightShadowBuffer[RowOffset + ColumnOffset].Reserved = Opacity;
    }
  }
}

564 565 566
STATIC
VOID
InternalBootPickerEntryDraw (
567 568 569 570 571 572 573 574
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN     INT64                   BaseX,
  IN     INT64                   BaseY,
  IN     UINT32                  OffsetX,
  IN     UINT32                  OffsetY,
  IN     UINT32                  Width,
575 576
  IN     UINT32                  Height,
  IN     UINT8                   Opacity
577 578 579 580 581 582 583 584 585 586 587
  )
{
  CONST GUI_VOLUME_ENTRY *Entry;
  CONST GUI_IMAGE        *EntryIcon;
  CONST GUI_IMAGE        *Label;

  ASSERT (This != NULL);
  ASSERT (DrawContext != NULL);
  ASSERT (Context != NULL);

  Entry       = BASE_CR (This, GUI_VOLUME_ENTRY, Hdr.Obj);
V
vit9696 已提交
588 589 590 591 592
  /*if (mBootPickerImageIndex < 5) {
    EntryIcon = &((BOOT_PICKER_GUI_CONTEXT *) DrawContext->GuiContext)->Poof[mBootPickerImageIndex];
  } else */{
    EntryIcon   = &Entry->EntryIcon;
  }
593 594 595 596
  Label       = &Entry->Label;
  //
  // Draw the icon horizontally centered.
  //
597
  ASSERT (EntryIcon != NULL);
598
  ASSERT_EQUALS (EntryIcon->Width,  This->Width);
599

600 601 602 603 604 605 606 607 608 609 610 611 612
  if (OffsetY < EntryIcon->Height) {
    GuiDrawToBuffer (
      EntryIcon,
      Opacity,
      DrawContext,
      BaseX,
      BaseY,
      OffsetX,
      OffsetY,
      Width,
      Height
      );
  }
613 614 615
  //
  // Draw the label horizontally centered.
  //
616 617 618 619 620 621 622

  //
  // FIXME: Apple allows the label to be up to 340px wide,
  // but OpenCanopy can't display it now (it would overlap adjacent entries)
  //
  //ASSERT (Label->Width  <= BOOT_ENTRY_DIMENSION * DrawContext->Scale);
  ASSERT (Label->Height <= BOOT_ENTRY_LABEL_HEIGHT * DrawContext->Scale);
623 624 625

  GuiDrawChildImage (
    Label,
626
    Opacity,
627 628 629
    DrawContext,
    BaseX,
    BaseY,
630
    Entry->LabelOffset,
631
    This->Height - Label->Height,
632 633 634
    OffsetX,
    OffsetY,
    Width,
635
    Height
636 637
    );
  //
638 639 640 641 642 643 644 645 646
  // If the label needs scrolling, draw a second one to get a wraparound effect.
  //
  if (Entry->Label.Width > This->Width) {
    GuiDrawChildImage (
      Label,
      Opacity,
      DrawContext,
      BaseX,
      BaseY,
647
      (INT64) Entry->LabelOffset + Entry->Label.Width + BOOT_LABEL_WRAPAROUND_PADDING * DrawContext->Scale,
648 649 650 651 652 653
      This->Height - Label->Height,
      OffsetX,
      OffsetY,
      Width,
      Height
      );
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683

    if (Entry->ShowLeftShadow) {
      GuiDrawChildImage (
        &mBootPickerLabelLeftShadowImage,
        Opacity,
        DrawContext,
        BaseX,
        BaseY,
        0,
        This->Height - mBootPickerLabelLeftShadowImage.Height,
        OffsetX,
        OffsetY,
        Width,
        Height
        );
    }

    GuiDrawChildImage (
      &mBootPickerLabelRightShadowImage,
      Opacity,
      DrawContext,
      BaseX,
      BaseY,
      This->Width - mBootPickerLabelRightShadowImage.Width,
      This->Height - mBootPickerLabelRightShadowImage.Height,
      OffsetX,
      OffsetY,
      Width,
      Height
      );
684 685
  }
  //
686 687
  // There should be no children.
  //
688
  ASSERT (This->NumChildren == 0);
689 690 691 692 693
}

STATIC
GUI_OBJ *
InternalBootPickerEntryPtrEvent (
694 695 696 697 698
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN     INT64                   BaseX,
  IN     INT64                   BaseY,
699
  IN     CONST GUI_PTR_EVENT     *Event
700 701 702 703
  )
{
  GUI_VOLUME_ENTRY        *Entry;
  BOOLEAN                 IsHit;
704 705
  UINT32                  OffsetX;
  UINT32                  OffsetY;
706

707 708 709 710 711 712
  OffsetX = (UINT32) (Event->Pos.Pos.X - BaseX);
  OffsetY = (UINT32) (Event->Pos.Pos.Y - BaseY);

  ASSERT (Event->Type == GuiPointerPrimaryDown
       || Event->Type == GuiPointerPrimaryUp
       || Event->Type == GuiPointerPrimaryDoubleClick);
713 714 715 716

  Entry = BASE_CR (This, GUI_VOLUME_ENTRY, Hdr.Obj);

  IsHit = GuiClickableIsHit (
V
vit9696 已提交
717
            &Entry->EntryIcon,
718 719
            OffsetX,
            OffsetY
720 721 722 723 724
            );
  if (!IsHit) {
    return This;
  }

725
  if (Event->Type == GuiPointerPrimaryDown) {
726
    if (mBootPicker.SelectedIndex != Entry->Index) {
727 728 729 730 731 732
      ASSERT (Entry->Hdr.Parent == &mBootPicker.Hdr.Obj);
      InternalBootPickerChangeEntry (
        &mBootPicker,
        DrawContext,
        BaseX - This->OffsetX,
        BaseY - This->OffsetY,
733
        Entry->Index
734 735
        );
    }
736
  } else if (Event->Type == GuiPointerPrimaryDoubleClick) {
737 738 739 740
    //
    // This must be ensured because the UI directs Move/Up events to the object
    // Down had been sent to.
    //
741
    ASSERT (mBootPicker.SelectedIndex == Entry->Index);
742
    Context->ReadyToBoot = TRUE;
743 744 745 746
  }
  //
  // There should be no children.
  //
747
  ASSERT (This->NumChildren == 0);
748 749 750 751
  return This;
}

VOID
752
InternalBootPickerSelectorBackgroundDraw (
753 754 755 756 757 758 759 760
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN     INT64                   BaseX,
  IN     INT64                   BaseY,
  IN     UINT32                  OffsetX,
  IN     UINT32                  OffsetY,
  IN     UINT32                  Width,
761 762
  IN     UINT32                  Height,
  IN     UINT8                   Opacity
763 764
  )
{
765
  CONST GUI_IMAGE *BackgroundImage;
766 767 768 769 770

  ASSERT (This != NULL);
  ASSERT (DrawContext != NULL);
  ASSERT (Context != NULL);

771
  BackgroundImage = &Context->Icons[ICON_SELECTED][ICON_TYPE_BASE];
772

773 774
  ASSERT_EQUALS (This->Width,  BackgroundImage->Width);
  ASSERT_EQUALS (This->Height, BackgroundImage->Height);
775

776
  GuiDrawToBuffer (
777 778
    BackgroundImage,
    Opacity,
779 780 781 782 783 784
    DrawContext,
    BaseX,
    BaseY,
    OffsetX,
    OffsetY,
    Width,
785
    Height
786 787 788 789
    );
  //
  // There should be no children.
  //
790
  ASSERT (This->NumChildren == 0);
791 792
}

793 794
GUI_OBJ *
InternalBootPickerSelectorPtrEvent (
795 796 797 798 799
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN     INT64                   BaseX,
  IN     INT64                   BaseY,
800
  IN     CONST GUI_PTR_EVENT     *Event
801 802
  )
{
803
  UINT8 Result;
804

805 806
  Result = InternalCommonSimpleButtonPtrEvent (
    This,
807
    DrawContext,
808
    Context,
809 810
    BaseX,
    BaseY,
811
    Event
812
    );
813 814 815 816 817 818 819 820 821 822 823 824
  switch (Result) {
    case CommonPtrNotHit:
    {
      return NULL;
    }
    
    case CommonPtrAction:
    {
      Context->ReadyToBoot = TRUE;
      //
      // Falthrough to 'hit' case.
      //
825 826
    }

827 828 829 830
    case CommonPtrHit:
    {
      break;
    }
831 832 833 834 835
  }

  return This;
}

836 837 838 839 840 841 842
GUI_OBJ *
InternalBootPickerLeftScrollPtrEvent (
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN     INT64                   BaseX,
  IN     INT64                   BaseY,
843
  IN     CONST GUI_PTR_EVENT     *Event
844 845
  )
{
846
  UINT8                  Result;
847

848 849 850
  INT64                  BootPickerX;
  INT64                  BootPickerY;
  CONST GUI_VOLUME_ENTRY *SelectedEntry;
851

852 853 854
  if (This->Opacity == 0) {
    return NULL;
  }
855

856 857 858 859 860 861 862
  Result = InternalCommonSimpleButtonPtrEvent (
    This,
    DrawContext,
    Context,
    BaseX,
    BaseY,
    Event
863
    );
864 865 866 867 868 869 870 871
  switch (Result) {
    case CommonPtrNotHit:
    {
      return NULL;
    }
    
    case CommonPtrAction:
    {
872 873 874 875 876 877 878 879 880 881
      //
      // The view can only be scrolled when there are off-screen entries.
      //
      GuiGetBaseCoords (
        &mBootPicker.Hdr.Obj,
        DrawContext,
        &BootPickerX,
        &BootPickerY
        );
      //
882 883 884 885 886 887 888 889 890 891
      // Scroll the boot entry view by one spot.
      //
      InternalBootPickerScroll (
        &mBootPicker,
        DrawContext,
        BootPickerX,
        BootPickerY,
        (BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * Context->Scale
        );
      //
892 893 894
      // If the selected entry is pushed off-screen by scrolling, select the
      // appropriate neighbour entry.
      //
895
      SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
896
      if (mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.Width > mBootPickerContainer.Obj.Width) {
897 898 899 900
        //
        // The internal design ensures a selected entry cannot be off-screen,
        // scrolling offsets it by at most one spot.
        //
901 902 903 904 905 906 907
        InternalBootPickerSelectEntry (
          &mBootPicker,
          DrawContext,
          mBootPicker.SelectedIndex > 0
            ? mBootPicker.SelectedIndex - 1
            : mBootPicker.Hdr.Obj.NumChildren - 1
          );
908

909
          SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
910
          ASSERT (!(mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.Width > mBootPickerContainer.Obj.Width));
911 912
      }
      //
913 914
      // Falthrough to 'hit' case.
      //
915 916
    }

917 918 919 920
    case CommonPtrHit:
    {
      break;
    }
921 922 923 924 925 926 927 928 929 930 931 932
  }

  return This;
}

GUI_OBJ *
InternalBootPickerRightScrollPtrEvent (
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN     INT64                   BaseX,
  IN     INT64                   BaseY,
933
  IN     CONST GUI_PTR_EVENT     *Event
934 935
  )
{
936
  UINT8            Result;
937

938 939 940
  INT64            BootPickerX;
  INT64            BootPickerY;
  GUI_VOLUME_ENTRY *SelectedEntry;
941

942 943 944
  if (This->Opacity == 0) {
    return NULL;
  }
945

946 947 948 949 950 951 952
  Result = InternalCommonSimpleButtonPtrEvent (
    This,
    DrawContext,
    Context,
    BaseX,
    BaseY,
    Event
953
    );
954 955 956 957 958 959 960 961
  switch (Result) {
    case CommonPtrNotHit:
    {
      return NULL;
    }
    
    case CommonPtrAction:
    {
962 963 964 965 966 967 968 969 970 971
      //
      // The view can only be scrolled when there are off-screen entries.
      //
      GuiGetBaseCoords (
        &mBootPicker.Hdr.Obj,
        DrawContext,
        &BootPickerX,
        &BootPickerY
        );
      //
972 973 974 975 976 977 978 979 980 981
      // Scroll the boot entry view by one spot.
      //
      InternalBootPickerScroll (
        &mBootPicker,
        DrawContext,
        BootPickerX,
        BootPickerY,
        -(INT64) (BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * Context->Scale
        );
      //
982 983 984
      // If the selected entry is pushed off-screen by scrolling, select the
      // appropriate neighbour entry.
      //
985
      SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
986
      if (mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX < 0) {
987 988 989 990
        //
        // The internal design ensures a selected entry cannot be off-screen,
        // scrolling offsets it by at most one spot.
        //
991 992 993 994 995 996 997
        InternalBootPickerSelectEntry (
          &mBootPicker,
          DrawContext,
          mBootPicker.SelectedIndex + 1 < mBootPicker.Hdr.Obj.NumChildren
            ? mBootPicker.SelectedIndex + 1
            : 0
          );
998

999
        SelectedEntry = InternalGetVolumeEntry (mBootPicker.SelectedIndex);
1000
        ASSERT (!(mBootPicker.Hdr.Obj.OffsetX + SelectedEntry->Hdr.Obj.OffsetX < 0));
1001 1002
      }
      //
1003 1004
      // Falthrough to 'hit' case.
      //
1005 1006
    }

1007 1008 1009 1010
    case CommonPtrHit:
    {
      break;
    }
1011 1012 1013 1014 1015
  }

  return This;
}

1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
STATIC GUI_IMAGE mVersionLabelImage;

VOID
InternalVersionLabelDraw (
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN     INT64                   BaseX,
  IN     INT64                   BaseY,
  IN     UINT32                  OffsetX,
  IN     UINT32                  OffsetY,
  IN     UINT32                  Width,
  IN     UINT32                  Height,
  IN     UINT8                   Opacity
  )
{
  if (mVersionLabelImage.Buffer != NULL) {
    GuiDrawToBuffer (
      &mVersionLabelImage,
      Opacity,
      DrawContext,
      BaseX,
      BaseY,
      OffsetX,
      OffsetY,
      Width,
      Height
      );
  }
}

1047
VOID
1048
InternalBootPickerViewKeyEvent (
1049 1050 1051
  IN OUT GUI_OBJ                 *This,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
1052
  IN     CONST GUI_KEY_EVENT     *KeyEvent
1053 1054
  )
{
1055 1056 1057
  GUI_OBJ          *FocusChangedObj;
  GUI_VOLUME_ENTRY *Entry;

1058 1059 1060
  ASSERT (This != NULL);
  ASSERT (DrawContext != NULL);

1061 1062 1063 1064 1065 1066 1067 1068
  if (KeyEvent->OcKeyCode == OC_INPUT_VOICE_OVER) {
    DrawContext->GuiContext->PickerContext->ToggleVoiceOver (
      DrawContext->GuiContext->PickerContext,
      0
      );
    return;
  }

1069 1070 1071
  Entry = InternalGetVolumeEntry(mBootPicker.SelectedIndex);

  FocusChangedObj = InternalFocusKeyHandler (
1072
    DrawContext,
1073 1074
    Context,
    KeyEvent
1075
    );
1076 1077 1078 1079 1080 1081 1082
  if (FocusChangedObj == &mBootPicker.Hdr.Obj) {
    InternalStartAnimateLabel (DrawContext, Entry);
  } else if (FocusChangedObj != NULL) {
    if (!IsListEmpty (&mBootPickerLabelAnimation.Link)) {
      InternalStopAnimateLabel (DrawContext, Entry);
    }
  }
1083 1084
}

1085 1086 1087 1088 1089
VOID
InternalBootPickerFocus (
  IN     CONST GUI_OBJ        *This,
  IN OUT GUI_DRAWING_CONTEXT  *DrawContext,
  IN     BOOLEAN              Focus
1090 1091
  )
{
1092 1093 1094 1095
  if (!Focus) {
    mBootPickerSelectorContainer.Obj.Opacity = 0;
  } else {
    mBootPickerSelectorContainer.Obj.Opacity = 0xFF;
1096 1097 1098

    DrawContext->GuiContext->AudioPlaybackTimeout = 0;
    DrawContext->GuiContext->VoAction = CanopyVoSelectedEntry;
1099 1100
  }

1101 1102 1103 1104 1105
  GuiRequestDraw (
    (UINT32) (mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX),
    (UINT32) (mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY),
    mBootPickerSelectorContainer.Obj.Width,
    mBootPickerSelectorContainer.Obj.Height
1106 1107 1108
    );
}

1109 1110 1111 1112
BOOLEAN
InternalBootPickerExitLoop (
  IN OUT GUI_DRAWING_CONTEXT      *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT  *Context
1113 1114 1115 1116
  )
{
  ASSERT (Context != NULL);

1117 1118
  if (Context->ReadyToBoot) {
    ASSERT (Context->BootEntry == InternalGetVolumeEntry (mBootPicker.SelectedIndex)->Context);
1119 1120
  }

1121
  return Context->ReadyToBoot || Context->Refresh;
1122 1123
}

1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerSelectorBackground = {
  {
    0, 0, 0, 0, 0xFF,
    InternalBootPickerSelectorBackgroundDraw,
    NULL,
    GuiObjDelegatePtrEvent,
    NULL,
    0,
    NULL
  },
  &mBootPickerSelectorContainer.Obj
};
1136

1137
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CLICKABLE mBootPickerSelectorButton = {
1138 1139
  {
    {
1140 1141 1142
      0, 0, 0, 0, 0xFF,
      InternalCommonSimpleButtonDraw,
      NULL,
1143
      InternalBootPickerSelectorPtrEvent,
1144
      NULL,
1145 1146
      0,
      NULL
1147
    },
1148
    &mBootPickerSelectorContainer.Obj
1149
  },
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
  0,
  0
};

STATIC GUI_OBJ_CHILD *mBootPickerSelectorContainerChilds[] = {
  &mBootPickerSelectorBackground,
  &mBootPickerSelectorButton.Hdr
};

GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerSelectorContainer = {
  {
    0, 0, 0, 0, 0xFF,
    GuiObjDrawDelegate,
    NULL,
    GuiObjDelegatePtrEvent,
    NULL,
    ARRAY_SIZE (mBootPickerSelectorContainerChilds),
    mBootPickerSelectorContainerChilds
  },
  &mBootPickerContainer.Obj
1170 1171
};

1172
STATIC GUI_OBJ_CHILD *mBootPickerContainerChilds[] = {
1173
  &mBootPickerSelectorContainer,
1174
  &mBootPicker.Hdr
1175
};
1176

1177 1178
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerContainer = {
  {
1179
    0, 0, 0, 0, 0xFF,
1180
    GuiObjDrawDelegate,
1181
    NULL,
1182
    GuiObjDelegatePtrEvent,
1183
    NULL,
1184 1185
    ARRAY_SIZE (mBootPickerContainerChilds),
    mBootPickerContainerChilds
1186
  },
1187
  NULL
1188 1189
};

1190 1191 1192
GLOBAL_REMOVE_IF_UNREFERENCED GUI_VOLUME_PICKER mBootPicker = {
  {
    {
1193
      0, 0, 0, 0, 0xFF,
1194
      GuiObjDrawDelegate,
1195
      InternalBootPickerKeyEvent,
1196
      GuiObjDelegatePtrEvent,
1197
      InternalBootPickerFocus,
1198 1199
      0,
      NULL
1200 1201
    },
    &mBootPickerContainer.Obj
1202
  },
1203
  1
1204 1205
};

1206 1207 1208
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CLICKABLE mBootPickerLeftScroll = {
  {
    {
1209 1210 1211
      0, 0, 0, 0, 0xFF,
      InternalCommonSimpleButtonDraw,
      NULL,
1212
      InternalBootPickerLeftScrollPtrEvent,
1213
      NULL,
1214 1215
      0,
      NULL
1216
    },
1217
    NULL
1218
  },
1219 1220
  0,
  0
1221 1222 1223 1224 1225
};

GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CLICKABLE mBootPickerRightScroll = {
  {
    {
1226 1227 1228
      0, 0, 0, 0, 0xFF,
      InternalCommonSimpleButtonDraw,
      NULL,
1229
      InternalBootPickerRightScrollPtrEvent,
1230
      NULL,
1231 1232
      0,
      NULL
1233
    },
1234
    NULL
1235
  },
1236 1237
  0,
  0
1238 1239
};

1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
GLOBAL_REMOVE_IF_UNREFERENCED GUI_OBJ_CHILD mBootPickerVersionLabel = {
  {
    0, 0, 0, 0, 0xFF,
    InternalVersionLabelDraw,
    NULL,
    GuiObjDelegatePtrEvent,
    NULL,
    0,
    NULL
  },
  NULL
};

1253 1254
STATIC GUI_OBJ_CHILD *mBootPickerViewChilds[] = {
  &mBootPickerContainer,
1255
  &mCommonActionButtonsContainer,
1256
  &mBootPickerLeftScroll.Hdr,
1257 1258
  &mBootPickerRightScroll.Hdr,
  &mBootPickerVersionLabel
1259 1260
};

M
MikeBeaton 已提交
1261 1262 1263
STATIC GUI_OBJ_CHILD *mBootPickerViewChildsMinimal[] = {
  &mBootPickerContainer,
  &mBootPickerLeftScroll.Hdr,
1264 1265
  &mBootPickerRightScroll.Hdr,
  &mBootPickerVersionLabel
M
MikeBeaton 已提交
1266 1267
};

1268 1269 1270
GLOBAL_REMOVE_IF_UNREFERENCED GUI_VIEW_CONTEXT mBootPickerViewContext = {
  InternalCommonViewDraw,
  InternalCommonViewPtrEvent,
1271
  ARRAY_SIZE (mBootPickerViewChilds),
1272 1273 1274 1275 1276 1277
  mBootPickerViewChilds,
  InternalBootPickerViewKeyEvent,
  InternalGetCursorImage,
  InternalBootPickerExitLoop,
  mBootPickerFocusList,
  ARRAY_SIZE (mBootPickerFocusList)
1278 1279
};

M
MikeBeaton 已提交
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
GLOBAL_REMOVE_IF_UNREFERENCED GUI_VIEW_CONTEXT mBootPickerViewContextMinimal = {
  InternalCommonViewDraw,
  InternalCommonViewPtrEvent,
  ARRAY_SIZE (mBootPickerViewChildsMinimal),
  mBootPickerViewChildsMinimal,
  InternalBootPickerViewKeyEvent,
  InternalGetCursorImage,
  InternalBootPickerExitLoop,
  mBootPickerFocusListMinimal,
  ARRAY_SIZE (mBootPickerFocusListMinimal)
};

V
vit9696 已提交
1292
STATIC
1293
EFI_STATUS
V
vit9696 已提交
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
CopyLabel (
  OUT GUI_IMAGE       *Destination,
  IN  CONST GUI_IMAGE *Source
  )
{
  Destination->Width = Source->Width;
  Destination->Height = Source->Height;
  Destination->Buffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) AllocateCopyPool (
    sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * Source->Width * Source->Height,
    Source->Buffer
    );

  if (Destination->Buffer == NULL) {
1307
    return EFI_OUT_OF_RESOURCES;
V
vit9696 已提交
1308 1309
  }

1310
  return EFI_SUCCESS;
V
vit9696 已提交
1311 1312
}

1313
EFI_STATUS
1314
BootPickerEntriesSet (
V
vit9696 已提交
1315
  IN OC_PICKER_CONTEXT              *Context,
1316
  IN BOOT_PICKER_GUI_CONTEXT        *GuiContext,
V
vit9696 已提交
1317
  IN OC_BOOT_ENTRY                  *Entry,
1318
  IN UINT8                          EntryIndex
1319 1320
  )
{
1321 1322 1323 1324 1325
  EFI_STATUS                  Status;
  GUI_VOLUME_ENTRY            *VolumeEntry;
  CONST GUI_IMAGE             *SuggestedIcon;
  CONST GUI_VOLUME_ENTRY      *PrevEntry;
  UINT32                      IconFileSize;
1326
  UINT32                      IconTypeIndex;
1327 1328
  VOID                        *IconFileData;
  BOOLEAN                     UseVolumeIcon;
1329
  BOOLEAN                     UseFlavourIcon;
1330 1331 1332
  BOOLEAN                     UseDiskLabel;
  BOOLEAN                     UseGenericLabel;
  BOOLEAN                     Result;
1333 1334
  CHAR16                      *EntryName;
  UINTN                       EntryNameLength;
1335 1336
  CHAR8                       *FlavourNameStart;
  CHAR8                       *FlavourNameEnd;
1337 1338

  ASSERT (GuiContext != NULL);
V
vit9696 已提交
1339
  ASSERT (Entry != NULL);
1340
  ASSERT (EntryIndex < mBootPicker.Hdr.Obj.NumChildren);
V
vit9696 已提交
1341 1342 1343 1344

  DEBUG ((DEBUG_INFO, "OCUI: Console attributes: %d\n", Context->ConsoleAttributes));

  UseVolumeIcon   = (Context->PickerAttributes & OC_ATTR_USE_VOLUME_ICON) != 0;
1345
  UseFlavourIcon  = (Context->PickerAttributes & OC_ATTR_USE_FLAVOUR_ICON) != 0;
V
vit9696 已提交
1346 1347 1348 1349
  UseDiskLabel    = (Context->PickerAttributes & OC_ATTR_USE_DISK_LABEL_FILE) != 0;
  UseGenericLabel = (Context->PickerAttributes & OC_ATTR_USE_GENERIC_LABEL_IMAGE) != 0;

  DEBUG ((DEBUG_INFO, "OCUI: UseDiskLabel: %d, UseGenericLabel: %d\n", UseDiskLabel, UseGenericLabel));
1350 1351 1352

  VolumeEntry = AllocateZeroPool (sizeof (*VolumeEntry));
  if (VolumeEntry == NULL) {
1353
    return EFI_OUT_OF_RESOURCES;
1354 1355
  }

V
vit9696 已提交
1356
  if (UseDiskLabel) {
1357
    Status = Context->GetEntryLabelImage (
V
vit9696 已提交
1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
      Context,
      Entry,
      GuiContext->Scale,
      &IconFileData,
      &IconFileSize
      );
    if (!EFI_ERROR (Status)) {
      Status = GuiLabelToImage (
        &VolumeEntry->Label,
        IconFileData,
        IconFileSize,
1369
        GuiContext->Scale,
1370
        GuiContext->LightBackground
V
vit9696 已提交
1371 1372 1373 1374 1375 1376 1377 1378
        );
    }
  } else {
    Status = EFI_UNSUPPORTED;
  }

  if (EFI_ERROR (Status) && UseGenericLabel) {
    switch (Entry->Type) {
1379 1380
      case OC_BOOT_APPLE_OS:
        Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_APPLE]);
V
vit9696 已提交
1381
        break;
1382
      case OC_BOOT_APPLE_FW_UPDATE:
V
vit9696 已提交
1383
      case OC_BOOT_APPLE_RECOVERY:
1384 1385 1386 1387
        Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_APPLE_RECOVERY]);
        break;
      case OC_BOOT_APPLE_TIME_MACHINE:
        Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_APPLE_TIME_MACHINE]);
V
vit9696 已提交
1388 1389
        break;
      case OC_BOOT_WINDOWS:
1390 1391 1392 1393 1394 1395 1396
        Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_WINDOWS]);
        break;
      case OC_BOOT_EXTERNAL_OS:
        Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_OTHER]);
        break;
      case OC_BOOT_RESET_NVRAM:
        Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_RESET_NVRAM]);
V
vit9696 已提交
1397
        break;
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
      case OC_BOOT_TOGGLE_SIP:
        ASSERT (
          StrCmp (Entry->Name, OC_MENU_SIP_IS_DISABLED) == 0 ||
          StrCmp (Entry->Name, OC_MENU_SIP_IS_ENABLED) == 0
          );
        if (StrCmp (Entry->Name, OC_MENU_SIP_IS_DISABLED) == 0) {
          Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_SIP_IS_DISABLED]);
        } else {
          Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_SIP_IS_ENABLED]);
        }
        break;
V
vit9696 已提交
1409
      case OC_BOOT_EXTERNAL_TOOL:
1410
        if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_RESET_NVRAM) != NULL) {
1411
          Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_RESET_NVRAM]);
1412
        } else if (OcAsciiStriStr (Entry->Flavour, OC_FLAVOUR_ID_UEFI_SHELL) != NULL) {
1413
          Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_SHELL]);
V
vit9696 已提交
1414
        } else {
1415
          Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_TOOL]);
V
vit9696 已提交
1416 1417
        }
        break;
1418 1419
      case OC_BOOT_UNKNOWN:
        Status = CopyLabel (&VolumeEntry->Label, &GuiContext->Labels[LABEL_GENERIC_HDD]);
V
vit9696 已提交
1420 1421
        break;
      default:
1422
        DEBUG ((DEBUG_WARN, "OCUI: Entry kind %d unsupported for label\n", Entry->Type));
1423
        return EFI_UNSUPPORTED;
V
vit9696 已提交
1424
    }
1425 1426
  }

V
vit9696 已提交
1427
  if (EFI_ERROR (Status)) {
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442
    EntryName       = Entry->Name;
    EntryNameLength = StrLen (Entry->Name);
    if (Entry->IsFolder) {
      EntryName = AllocatePool (EntryNameLength * sizeof (CHAR16) + L_STR_SIZE (L" (dmg)"));
      if (EntryName != NULL) {
        CopyMem (EntryName, Entry->Name, EntryNameLength * sizeof (CHAR16));
        CopyMem (&EntryName[EntryNameLength], L" (dmg)", L_STR_SIZE (L" (dmg)"));
        EntryNameLength += L_STR_LEN (L" (dmg)");
      } else {
        EntryName = Entry->Name;
      }
    }

    ASSERT (StrLen (EntryName) == EntryNameLength);

V
vit9696 已提交
1443 1444 1445
    Result = GuiGetLabel (
      &VolumeEntry->Label,
      &GuiContext->FontContext,
1446 1447
      EntryName,
      EntryNameLength,
1448
      GuiContext->LightBackground
V
vit9696 已提交
1449
      );
1450 1451 1452 1453 1454
    
    if (EntryName != Entry->Name) {
      FreePool (EntryName);
    }

V
vit9696 已提交
1455 1456
    if (!Result) {
      DEBUG ((DEBUG_WARN, "OCUI: label failed\n"));
1457
      return EFI_UNSUPPORTED;
V
vit9696 已提交
1458 1459 1460 1461 1462
    }
  }

  VolumeEntry->Context = Entry;

1463 1464 1465 1466 1467 1468 1469 1470
  //
  // Load volume icons when allowed.
  // Do not load volume icons for Time Machine entries unless explicitly enabled.
  // This works around Time Machine icon style incompatibilities.
  //
  if (UseVolumeIcon
    && (Entry->Type != OC_BOOT_APPLE_TIME_MACHINE
      || (Context->PickerAttributes & OC_ATTR_HIDE_THEMED_ICONS) == 0)) {
1471
    Status = Context->GetEntryIcon (Context, Entry, &IconFileData, &IconFileSize);
1472

V
vit9696 已提交
1473
    if (!EFI_ERROR (Status)) {
1474 1475 1476 1477 1478
      Status = GuiIcnsToImageIcon (
        &VolumeEntry->EntryIcon,
        IconFileData,
        IconFileSize,
        GuiContext->Scale,
1479 1480
        BOOT_ENTRY_ICON_DIMENSION,
        BOOT_ENTRY_ICON_DIMENSION,
1481
        FALSE
1482
        );
V
vit9696 已提交
1483 1484 1485
      FreePool (IconFileData);
      if (!EFI_ERROR (Status)) {
        VolumeEntry->CustomIcon = TRUE;
1486 1487
      } else {
        DEBUG ((DEBUG_INFO, "OCUI: Failed to convert icon - %r\n", Status));
V
vit9696 已提交
1488 1489
      }
    }
1490
  } else {
V
vit9696 已提交
1491 1492 1493
    Status = EFI_UNSUPPORTED;
  }

1494 1495 1496 1497
  //
  // Flavour system is used internally for icon priorities even when
  // user-specified flavours from .contentFlavour are not being read
  //
V
vit9696 已提交
1498
  if (EFI_ERROR (Status)) {
1499 1500
    ASSERT (Entry->Flavour != NULL);

1501
    IconTypeIndex = Entry->IsExternal ? ICON_TYPE_EXTERNAL : ICON_TYPE_BASE;
1502

1503 1504 1505 1506 1507 1508 1509
    FlavourNameEnd = Entry->Flavour - 1;
    do
    {
      for (FlavourNameStart = ++FlavourNameEnd; *FlavourNameEnd != '\0' && *FlavourNameEnd != ':'; ++FlavourNameEnd);

      Status = InternalGetFlavourIcon (
        GuiContext,
1510
        Context->StorageContext,
1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523
        FlavourNameStart,
        FlavourNameEnd - FlavourNameStart,
        IconTypeIndex,
        UseFlavourIcon,
        &VolumeEntry->EntryIcon,
        &VolumeEntry->CustomIcon
        );
    } while (EFI_ERROR (Status) && *FlavourNameEnd != '\0');

    if (EFI_ERROR (Status))
    {
      SuggestedIcon = NULL;

1524
      if (Entry->Type == OC_BOOT_EXTERNAL_OS) {
1525
          SuggestedIcon = &GuiContext->Icons[ICON_OTHER][IconTypeIndex];
1526
      } else if (Entry->Type == OC_BOOT_EXTERNAL_TOOL || (Entry->Type & OC_BOOT_SYSTEM) != 0) {
1527
          SuggestedIcon = &GuiContext->Icons[ICON_TOOL][IconTypeIndex];
1528
      }
1529

1530 1531 1532
      if (SuggestedIcon == NULL || SuggestedIcon->Buffer == NULL) {
          SuggestedIcon = &GuiContext->Icons[ICON_GENERIC_HDD][IconTypeIndex];
      }
1533

1534 1535 1536
      CopyMem (&VolumeEntry->EntryIcon, SuggestedIcon, sizeof (VolumeEntry->EntryIcon));
    } else {
      DEBUG ((DEBUG_INFO, "OCUI: Using flavour icon, custom: %u\n", VolumeEntry->CustomIcon));
V
vit9696 已提交
1537
    }
1538 1539 1540
  }

  VolumeEntry->Hdr.Parent       = &mBootPicker.Hdr.Obj;
1541 1542
  VolumeEntry->Hdr.Obj.Width    = BOOT_ENTRY_ICON_DIMENSION * GuiContext->Scale;
  VolumeEntry->Hdr.Obj.Height   = (BOOT_ENTRY_HEIGHT - BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale;
1543
  VolumeEntry->Hdr.Obj.Opacity  = 0xFF;
1544 1545
  VolumeEntry->Hdr.Obj.Draw     = InternalBootPickerEntryDraw;
  VolumeEntry->Hdr.Obj.PtrEvent = InternalBootPickerEntryPtrEvent;
1546 1547
  VolumeEntry->Hdr.Obj.NumChildren = 0;
  VolumeEntry->Hdr.Obj.Children    = NULL;
1548 1549 1550
  if (VolumeEntry->Hdr.Obj.Width > VolumeEntry->Label.Width) {
    VolumeEntry->LabelOffset = (INT16) ((VolumeEntry->Hdr.Obj.Width - VolumeEntry->Label.Width) / 2);
  }
1551 1552

  if (EntryIndex > 0) {
1553
    PrevEntry = InternalGetVolumeEntry (EntryIndex - 1);
1554
    VolumeEntry->Hdr.Obj.OffsetX = PrevEntry->Hdr.Obj.OffsetX + (BOOT_ENTRY_DIMENSION + BOOT_ENTRY_SPACE) * GuiContext->Scale;
1555 1556 1557 1558 1559
  } else {
    //
    // Account for the selector background.
    //
    VolumeEntry->Hdr.Obj.OffsetX = BOOT_ENTRY_ICON_SPACE * GuiContext->Scale;
1560 1561
  }

1562 1563
  VolumeEntry->Hdr.Obj.OffsetY = BOOT_ENTRY_ICON_SPACE * GuiContext->Scale;

1564 1565
  mBootPicker.Hdr.Obj.Children[EntryIndex] = &VolumeEntry->Hdr;
  VolumeEntry->Index = EntryIndex;
1566 1567
  mBootPicker.Hdr.Obj.Width   += (BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * GuiContext->Scale;
  mBootPicker.Hdr.Obj.OffsetX -= (BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * GuiContext->Scale / 2;
1568

1569
  return EFI_SUCCESS;
1570 1571 1572 1573 1574 1575 1576 1577 1578 1579
}

VOID
InternalBootPickerEntryDestruct (
  IN GUI_VOLUME_ENTRY  *Entry
  )
{
  ASSERT (Entry != NULL);
  ASSERT (Entry->Label.Buffer != NULL);

V
vit9696 已提交
1580 1581 1582 1583
  if (Entry->CustomIcon) {
    FreePool (Entry->EntryIcon.Buffer);
  }

1584 1585 1586 1587
  FreePool (Entry->Label.Buffer);
  FreePool (Entry);
}

V
vit9696 已提交
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606
STATIC GUI_INTERPOLATION mBpAnimInfoImageList;

VOID
InitBpAnimImageList (
  IN GUI_INTERPOL_TYPE  Type,
  IN UINT64             StartTime,
  IN UINT64             Duration
  )
{
  mBpAnimInfoImageList.Type       = Type;
  mBpAnimInfoImageList.StartTime  = StartTime;
  mBpAnimInfoImageList.Duration   = Duration;
  mBpAnimInfoImageList.StartValue = 0;
  mBpAnimInfoImageList.EndValue   = 5;
}


BOOLEAN
InternalBootPickerAnimateImageList (
1607 1608 1609
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     UINT64                  CurrentTime
V
vit9696 已提交
1610 1611
  )
{
1612
#if 0
V
vit9696 已提交
1613 1614 1615 1616 1617 1618
  GUI_VOLUME_ENTRY *Entry;
  CONST GUI_IMAGE  *EntryIcon;

  Entry       = BASE_CR (&mBootPicker.Hdr.Obj, GUI_VOLUME_ENTRY, Hdr.Obj);
  EntryIcon   = &Entry->EntryIcon;

1619
  mBootPickerImageIndex++;
V
vit9696 已提交
1620
  mBootPickerImageIndex = (UINT8)GuiGetInterpolatedValue (&mBpAnimInfoImageList, CurrentTime);
1621
  Entry->EntryIcon = &((GUI_IMAGE*)Context)[mBootPickerImageIndex];
V
vit9696 已提交
1622 1623 1624 1625
  GuiRedrawObject (
    &mBootPicker.Hdr.Obj,
    DrawContext,
    mBootPicker.Hdr.Obj.OffsetX,
1626
    mBootPicker.Hdr.Obj.OffsetY
V
vit9696 已提交
1627 1628 1629 1630 1631
    );

  if (mBootPickerImageIndex == mBpAnimInfoImageList.EndValue) {
    return TRUE;
  }
1632
#endif
V
vit9696 已提交
1633 1634 1635
  return FALSE;
}

1636 1637 1638 1639 1640 1641 1642 1643
STATIC GUI_INTERPOLATION mBpAnimInfoSinMove = {
  GuiInterpolTypeSmooth,
  0,
  25,
  0,
  0,
  0
};
1644 1645

VOID
1646
InitBpAnimIntro (
1647
  IN CONST GUI_DRAWING_CONTEXT  *DrawContext
1648 1649
  )
{
1650
  mBpAnimInfoSinMove.EndValue = 35 * DrawContext->Scale;
1651 1652
  //
  // FIXME: This assumes that only relative changes of X are performed on
1653
  //        mBootPickerContainer between animation initialisation and start.
1654
  //
1655
  mBootPickerContainer.Obj.OffsetX += 35 * DrawContext->Scale;
1656 1657 1658
}

BOOLEAN
1659
InternalBootPickerAnimateIntro (
1660 1661 1662
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     UINT64                  CurrentTime
1663 1664
  )
{
1665
  STATIC UINT32 PrevSine = 0;
1666

1667
  UINT8  Opacity;
1668
  UINT32 InterpolVal;
1669
  UINT32 DeltaSine;
1670 1671 1672

  ASSERT (DrawContext != NULL);

1673 1674 1675 1676 1677 1678
  Opacity = (UINT8) GuiGetInterpolatedValue (
    &mCommonIntroOpacityInterpol,
    CurrentTime
    );
  mBootPickerContainer.Obj.Opacity = Opacity;
  //
1679 1680 1681 1682 1683 1684 1685 1686 1687
  // Animate the scroll buttons based on their active state.
  //
  if (mBootPicker.Hdr.Obj.OffsetX < 0) {
    mBootPickerLeftScroll.Hdr.Obj.Opacity = Opacity;
  }
  if (mBootPicker.Hdr.Obj.OffsetX + mBootPicker.Hdr.Obj.Width > mBootPickerContainer.Obj.Width) {
    mBootPickerRightScroll.Hdr.Obj.Opacity = Opacity;
  }
  //
1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706
  // If PasswordView already faded-in the action buttons, skip them.
  //
  if (mCommonActionButtonsContainer.Obj.Opacity != 0xFF) {
    mCommonActionButtonsContainer.Obj.Opacity = Opacity;
    //
    // The view is constructed such that the action buttons are always fully
    // visible.
    //
    ASSERT (mCommonActionButtonsContainer.Obj.OffsetX >= 0);
    ASSERT (mCommonActionButtonsContainer.Obj.OffsetY >= 0);
    ASSERT (mCommonActionButtonsContainer.Obj.OffsetX + mCommonActionButtonsContainer.Obj.Width <= DrawContext->Screen.Width);
    ASSERT (mCommonActionButtonsContainer.Obj.OffsetY + mCommonActionButtonsContainer.Obj.Height <= DrawContext->Screen.Height);
    GuiRequestDraw (
      (UINT32) mCommonActionButtonsContainer.Obj.OffsetX,
      (UINT32) mCommonActionButtonsContainer.Obj.OffsetY,
      mCommonActionButtonsContainer.Obj.Width,
      mCommonActionButtonsContainer.Obj.Height
      );
  }
1707

1708
  InterpolVal = GuiGetInterpolatedValue (&mBpAnimInfoSinMove, CurrentTime);
1709
  DeltaSine = InterpolVal - PrevSine;
1710
  mBootPickerContainer.Obj.OffsetX -= DeltaSine;
1711
  PrevSine = InterpolVal;
1712 1713 1714 1715
  //
  // Draw the full dimension of the inner container to implicitly cover the
  // scroll buttons with the off-screen entries.
  //
1716
  GuiRequestDrawCrop (
1717
    DrawContext,
1718
    mBootPickerContainer.Obj.OffsetX + mBootPicker.Hdr.Obj.OffsetX,
1719
    mBootPickerContainer.Obj.OffsetY + mBootPicker.Hdr.Obj.OffsetY,
1720
    mBootPicker.Hdr.Obj.Width + DeltaSine,
1721
    mBootPicker.Hdr.Obj.Height
1722
    );
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
  
  ASSERT (mBpAnimInfoSinMove.Duration == mCommonIntroOpacityInterpol.Duration);
  return CurrentTime - mBpAnimInfoSinMove.StartTime >= mBpAnimInfoSinMove.Duration;
}

STATIC GUI_INTERPOLATION mBootPickerTimeoutOpacityInterpolDown = {
  GuiInterpolTypeLinear,
  0,
  125,
  0xFF,
  0x50,
  0
};

STATIC GUI_INTERPOLATION mBootPickerTimeoutOpacityInterpolUp = {
  GuiInterpolTypeLinear,
  0,
  125,
  0x50,
  0xFF,
  20
};

BOOLEAN
InternalBootPickerAnimateTimeout (
  IN     BOOT_PICKER_GUI_CONTEXT *Context,
  IN OUT GUI_DRAWING_CONTEXT     *DrawContext,
  IN     UINT64                  CurrentTime
  )
{
  STATIC GUI_INTERPOLATION *CurInterpol  = &mBootPickerTimeoutOpacityInterpolDown;
  STATIC GUI_INTERPOLATION *NextInterpol = &mBootPickerTimeoutOpacityInterpolUp;

  GUI_INTERPOLATION *Temp;

  if (DrawContext->TimeOutSeconds > 0) {
    mBootPickerSelectorButton.Hdr.Obj.Opacity = (UINT8) GuiGetInterpolatedValue (
      CurInterpol,
      CurrentTime
      );
  } else {
    mBootPickerSelectorButton.Hdr.Obj.Opacity = 0xFF;
  }
1766 1767 1768 1769 1770
  //
  // The view is constructed such that the action buttons are always fully
  // visible.
  //
  GuiRequestDraw (
1771 1772 1773 1774
    (UINT32) (mBootPickerContainer.Obj.OffsetX + mBootPickerSelectorContainer.Obj.OffsetX + mBootPickerSelectorButton.Hdr.Obj.OffsetX),
    (UINT32) (mBootPickerContainer.Obj.OffsetY + mBootPickerSelectorContainer.Obj.OffsetY + mBootPickerSelectorButton.Hdr.Obj.OffsetY),
    mBootPickerSelectorButton.Hdr.Obj.Width,
    mBootPickerSelectorButton.Hdr.Obj.Height
1775
    );
1776
  
1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788
  if (DrawContext->TimeOutSeconds == 0) {
    return TRUE;
  }
  
  if (CurrentTime - CurInterpol->StartTime >= CurInterpol->Duration + CurInterpol->HoldTime) {
    Temp         = CurInterpol;
    CurInterpol  = NextInterpol;
    NextInterpol = Temp;
    CurInterpol->StartTime = CurrentTime + 1;
  }

  return FALSE;
1789 1790
}

1791 1792 1793 1794 1795 1796
STATIC GUI_ANIMATION mBootPickerIntroAnimation = {
  INITIALIZE_LIST_HEAD_VARIABLE (mBootPickerIntroAnimation.Link),
  NULL,
  InternalBootPickerAnimateIntro
};

1797
EFI_STATUS
1798 1799 1800
BootPickerViewInitialize (
  OUT GUI_DRAWING_CONTEXT      *DrawContext,
  IN  BOOT_PICKER_GUI_CONTEXT  *GuiContext,
1801 1802
  IN  GUI_CURSOR_GET_IMAGE     GetCursorImage,
  IN  UINT8                    NumBootEntries
1803 1804
  )
{
1805 1806 1807 1808
  CONST GUI_IMAGE *SelectorBackgroundImage;
  CONST GUI_IMAGE *SelectorButtonImage;
  UINT32          ContainerMaxWidth;
  UINT32          ContainerWidthDelta;
1809 1810 1811
  UINTN           DestLen;
  CHAR16          *UString;
  BOOLEAN         Result;
1812

1813 1814 1815 1816
  ASSERT (DrawContext != NULL);
  ASSERT (GuiContext != NULL);
  ASSERT (GetCursorImage != NULL);

1817 1818 1819
  mKeyContext->KeyFilter = OC_PICKER_KEYS_FOR_PICKER;

  CommonViewInitialize (
1820
    DrawContext,
1821
    GuiContext,
M
MikeBeaton 已提交
1822 1823 1824
    (GuiContext->PickerContext->PickerAttributes & OC_ATTR_USE_MINIMAL_UI) == 0
      ? &mBootPickerViewContext
      : &mBootPickerViewContextMinimal
1825 1826
    );

1827
  mBackgroundImageOffsetX = DivS64x64Remainder (
1828
    (INT64) DrawContext->Screen.Width - DrawContext->GuiContext->Background.Width,
1829 1830 1831 1832
    2,
    NULL
    );
  mBackgroundImageOffsetY = DivS64x64Remainder (
1833
    (INT64) DrawContext->Screen.Height - DrawContext->GuiContext->Background.Height,
1834 1835 1836
    2,
    NULL
    );
1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
  
  SelectorBackgroundImage = &GuiContext->Icons[ICON_SELECTED][ICON_TYPE_BASE];
  SelectorButtonImage     = &GuiContext->Icons[ICON_SELECTOR][ICON_TYPE_BASE];

  mBootPickerSelectorBackground.Obj.OffsetX = 0;
  mBootPickerSelectorBackground.Obj.OffsetY = 0;
  mBootPickerSelectorBackground.Obj.Width   = SelectorBackgroundImage->Width;
  mBootPickerSelectorBackground.Obj.Height  = SelectorBackgroundImage->Height;

  ASSERT (SelectorBackgroundImage->Width >= SelectorButtonImage->Width);
  mBootPickerSelectorButton.Hdr.Obj.OffsetX = (SelectorBackgroundImage->Width - SelectorButtonImage->Width) / 2;
  mBootPickerSelectorButton.Hdr.Obj.OffsetY = SelectorBackgroundImage->Height + BOOT_SELECTOR_BUTTON_SPACE * DrawContext->Scale;
  mBootPickerSelectorButton.Hdr.Obj.Width   = SelectorButtonImage->Width;
  mBootPickerSelectorButton.Hdr.Obj.Height  = SelectorButtonImage->Height;
  mBootPickerSelectorButton.ImageId         = ICON_SELECTOR;
  mBootPickerSelectorButton.ImageState      = ICON_TYPE_BASE;

  mBootPickerSelectorContainer.Obj.OffsetX = 0;
  mBootPickerSelectorContainer.Obj.OffsetY = 0;
  mBootPickerSelectorContainer.Obj.Width   = SelectorBackgroundImage->Width;
  mBootPickerSelectorContainer.Obj.Height  = (UINT32) (mBootPickerSelectorButton.Hdr.Obj.OffsetY + mBootPickerSelectorButton.Hdr.Obj.Height);
1858

1859 1860 1861
  mBootPickerLeftScroll.Hdr.Obj.Height = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
  mBootPickerLeftScroll.Hdr.Obj.Width  = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
  mBootPickerLeftScroll.Hdr.Obj.OffsetX = BOOT_SCROLL_BUTTON_SPACE;
1862 1863 1864
  mBootPickerLeftScroll.Hdr.Obj.OffsetY = (DrawContext->Screen.Height - mBootPickerLeftScroll.Hdr.Obj.Height) / 2;
  mBootPickerLeftScroll.ImageId         = ICON_LEFT;
  mBootPickerLeftScroll.ImageState      = ICON_TYPE_BASE;
1865 1866 1867

  mBootPickerRightScroll.Hdr.Obj.Height = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
  mBootPickerRightScroll.Hdr.Obj.Width  = BOOT_SCROLL_BUTTON_DIMENSION * GuiContext->Scale;
1868 1869 1870 1871
  mBootPickerRightScroll.Hdr.Obj.OffsetX = DrawContext->Screen.Width - mBootPickerRightScroll.Hdr.Obj.Width - BOOT_SCROLL_BUTTON_SPACE;
  mBootPickerRightScroll.Hdr.Obj.OffsetY = (DrawContext->Screen.Height - mBootPickerRightScroll.Hdr.Obj.Height) / 2;
  mBootPickerRightScroll.ImageId         = ICON_RIGHT;
  mBootPickerRightScroll.ImageState      = ICON_TYPE_BASE;
1872 1873 1874 1875
  //
  // The boot entry container must precisely show a set of boot entries, i.e.
  // there may not be partial entries or extra padding.
  //
1876
  ContainerMaxWidth   = DrawContext->Screen.Width - mBootPickerLeftScroll.Hdr.Obj.Width - 2 * BOOT_SCROLL_BUTTON_SPACE - mBootPickerRightScroll.Hdr.Obj.Width - 2 * BOOT_SCROLL_BUTTON_SPACE;
1877 1878 1879 1880
  ContainerWidthDelta = (ContainerMaxWidth + BOOT_ENTRY_SPACE * GuiContext->Scale) % ((BOOT_ENTRY_WIDTH + BOOT_ENTRY_SPACE) * GuiContext->Scale);

  mBootPickerContainer.Obj.Height  = BOOT_SELECTOR_HEIGHT * GuiContext->Scale;
  mBootPickerContainer.Obj.Width   = ContainerMaxWidth - ContainerWidthDelta;
1881
  mBootPickerContainer.Obj.OffsetX = (DrawContext->Screen.Width - mBootPickerContainer.Obj.Width) / 2;
1882 1883 1884
  //
  // Center the icons and labels excluding the selector images vertically.
  //
1885 1886
  ASSERT ((DrawContext->Screen.Height - (BOOT_ENTRY_HEIGHT - BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale) / 2 - (BOOT_ENTRY_ICON_SPACE * GuiContext->Scale) == (DrawContext->Screen.Height - (BOOT_ENTRY_HEIGHT + BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale) / 2);
  mBootPickerContainer.Obj.OffsetY = (DrawContext->Screen.Height - (BOOT_ENTRY_HEIGHT + BOOT_ENTRY_ICON_SPACE) * GuiContext->Scale) / 2;
1887

1888
  mBootPicker.Hdr.Obj.Height  = BOOT_SELECTOR_HEIGHT * GuiContext->Scale;
1889 1890 1891 1892 1893
  //
  // Adding an entry will wrap around Width such that the first entry has no
  // padding.
  //
  mBootPicker.Hdr.Obj.Width   = 0U - (UINT32) (BOOT_ENTRY_SPACE * GuiContext->Scale);
1894 1895 1896 1897 1898
  //
  // Adding an entry will also shift OffsetX considering the added boot entry
  // space. This is not needed for the first, so initialise accordingly.
  //
  mBootPicker.Hdr.Obj.OffsetX = mBootPickerContainer.Obj.Width / 2 + (UINT32) (BOOT_ENTRY_SPACE * GuiContext->Scale) / 2;
1899
  mBootPicker.Hdr.Obj.OffsetY = 0;
1900

1901
  mBootPicker.SelectedIndex = 0;
1902

1903
  mBootPicker.Hdr.Obj.Children = AllocateZeroPool (NumBootEntries * sizeof (*mBootPicker.Hdr.Obj.Children));
1904 1905 1906
  if (mBootPicker.Hdr.Obj.Children == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
1907
  mBootPicker.Hdr.Obj.NumChildren = NumBootEntries;
1908

1909 1910 1911 1912 1913 1914 1915 1916 1917
  if (GuiContext->PickerContext->TitleSuffix == NULL) {
    mVersionLabelImage.Buffer = NULL;

    mBootPickerVersionLabel.Obj.Width   = 0;
    mBootPickerVersionLabel.Obj.Height  = 0;
    mBootPickerVersionLabel.Obj.OffsetX = 0;
    mBootPickerVersionLabel.Obj.OffsetY = 0;
  } else {
    DestLen = AsciiStrLen (GuiContext->PickerContext->TitleSuffix);
1918
    UString = AsciiStrCopyToUnicode (GuiContext->PickerContext->TitleSuffix, DestLen);
1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940

    Result = GuiGetLabel (
      &mVersionLabelImage,
      &GuiContext->FontContext,
      UString,
      DestLen,
      GuiContext->LightBackground
      );

    FreePool (UString);
    
    if (!Result) {
      DEBUG ((DEBUG_WARN, "OCUI: version label failed\n"));
      return Result;
    }

    mBootPickerVersionLabel.Obj.Width   = mVersionLabelImage.Width;
    mBootPickerVersionLabel.Obj.Height  = mVersionLabelImage.Height;
    mBootPickerVersionLabel.Obj.OffsetX = DrawContext->Screen.Width  - ((3 * mBootPickerVersionLabel.Obj.Width ) / 2);
    mBootPickerVersionLabel.Obj.OffsetY = DrawContext->Screen.Height - ((5 * mBootPickerVersionLabel.Obj.Height) / 2);
  }

V
vit9696 已提交
1941 1942 1943 1944 1945 1946 1947 1948 1949
  // TODO: animations should be tied to UI objects, not global
  // Each object has its own list of animations.
  // How to animate addition of one or more boot entries?
  // 1. MOVE:
  //    - Calculate the delta by which each entry moves to the left or to the right.
  //      ∆i = (N(added after) - N(added before))
  // Conditions for delta function:
  //

1950
  if (!GuiContext->DoneIntroAnimation) {
1951 1952 1953
    InitBpAnimIntro (DrawContext);
    InsertHeadList (&DrawContext->Animations, &mBootPickerIntroAnimation.Link);
    //
1954
    // Fade-in picker, scroll buttons, and action buttons.
1955
    //
1956 1957 1958
    mBootPickerContainer.Obj.Opacity       = 0;
    mBootPickerLeftScroll.Hdr.Obj.Opacity  = 0;
    mBootPickerRightScroll.Hdr.Obj.Opacity = 0;
1959

1960
    GuiContext->DoneIntroAnimation = TRUE;
1961
  } else {
1962 1963 1964 1965 1966
    //
    // The late code assumes the scroll buttons are visible by default.
    //
    mBootPickerLeftScroll.Hdr.Obj.Opacity  = 0xFF;
    mBootPickerRightScroll.Hdr.Obj.Opacity = 0xFF;
1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977
    //
    // Unhide action buttons immediately if they are not animated.
    //
    mCommonActionButtonsContainer.Obj.Opacity = 0xFF;
  }

  if (DrawContext->TimeOutSeconds > 0) {
    STATIC GUI_ANIMATION PickerAnim2;
    PickerAnim2.Context = NULL;
    PickerAnim2.Animate = InternalBootPickerAnimateTimeout;
    InsertHeadList (&DrawContext->Animations, &PickerAnim2.Link);
1978
  }
1979

1980 1981
  InternalInitialiseLabelShadows (DrawContext, GuiContext);

V
vit9696 已提交
1982 1983 1984 1985 1986 1987 1988 1989
  /*
  InitBpAnimImageList(GuiInterpolTypeLinear, 25, 25);
  STATIC GUI_ANIMATION PoofAnim;
  PoofAnim.Context = GuiContext->Poof;
  PoofAnim.Animate = InternalBootPickerAnimateImageList;
  InsertHeadList(&DrawContext->Animations, &PoofAnim.Link);
  */

1990
  return EFI_SUCCESS;
1991
}
1992

1993 1994
VOID
BootPickerViewLateInitialize (
1995 1996 1997
  IN OUT GUI_DRAWING_CONTEXT      *DrawContext,
  IN     BOOT_PICKER_GUI_CONTEXT  *GuiContext,
  IN     UINT8                    DefaultIndex
1998 1999 2000 2001
  )
{
  CONST GUI_VOLUME_ENTRY *BootEntry;

2002 2003
  ASSERT (DefaultIndex < mBootPicker.Hdr.Obj.NumChildren);

2004 2005
  mBootPicker.SelectedIndex = DefaultIndex;

2006
  //
2007 2008
  // If more entries than screen width, firstly align left-most entry
  // then scroll from there as needed to bring default entry on screen
2009
  //
2010 2011 2012
  if (mBootPicker.Hdr.Obj.OffsetX < 0) {
    mBootPicker.Hdr.Obj.OffsetX = 0;
    mBootPicker.Hdr.Obj.OffsetX = InternelBootPickerScrollSelected ();
2013 2014
  }

2015 2016 2017 2018 2019 2020 2021
  //
  // If the scroll buttons are hidden, the intro animation will update them
  // implicitly.
  //
  if (mBootPickerLeftScroll.Hdr.Obj.Opacity == 0xFF) {
    InternalUpdateScrollButtons ();
  }
2022
  InternalBootPickerSelectEntry (&mBootPicker, NULL, DefaultIndex);
2023 2024 2025
  BootEntry = InternalGetVolumeEntry (DefaultIndex);
  InternalStartAnimateLabel (DrawContext, BootEntry);
  GuiContext->BootEntry = BootEntry->Context;
2026 2027
}

2028 2029 2030 2031 2032 2033
VOID
BootPickerViewDeinitialize (
  IN OUT GUI_DRAWING_CONTEXT      *DrawContext,
  IN OUT BOOT_PICKER_GUI_CONTEXT  *GuiContext
  )
{
2034
  UINT32 Index;
2035

2036 2037 2038 2039 2040
  if (mVersionLabelImage.Buffer != NULL) {
    FreePool (mVersionLabelImage.Buffer);
    mVersionLabelImage.Buffer = NULL;
  }

2041
  for (Index = 0; Index < mBootPicker.Hdr.Obj.NumChildren; ++Index) {
2042 2043
    InternalBootPickerEntryDestruct (InternalGetVolumeEntry (Index));
  }
2044

2045 2046
  if (mBootPicker.Hdr.Obj.Children != NULL) {
    FreePool (mBootPicker.Hdr.Obj.Children);
2047
  }
2048
  mBootPicker.Hdr.Obj.NumChildren = 0;
2049

2050
  GuiViewDeinitialize (DrawContext, GuiContext);
2051
}