Skip to content

Latest commit

 

History

History
127 lines (96 loc) · 4.03 KB

File metadata and controls

127 lines (96 loc) · 4.03 KB

Hackathon Card Porting Guide

This document outlines the process for porting old hackathon branches to the new card architecture in DeskHog.

Overview

The main branch introduced a new extensible card system that allows dynamic card management through a web UI. Old branches need to be updated to use this architecture.

Step-by-Step Porting Process

1. Understand the New Architecture

The new system uses:

  • CardType enum - Defines available card types
  • CardDefinition - Metadata for each card type
  • Factory pattern - Lambda functions create card instances
  • InputHandler interface - Base class for cards that handle input
  • Dynamic card management - Cards can be added/removed via web UI

2. Analyze the Existing Implementation

For the Flappy Hog example:

  • Original: FlappyBirdGame class with direct integration in main.cpp
  • Had custom button handling and game loop in the LVGL task
  • Direct references in CardController

3. Create a Card Wrapper Class

Create a wrapper that implements InputHandler:

class FlappyHogCard : public InputHandler {
public:
    FlappyHogCard(lv_obj_t* parent);
    ~FlappyHogCard();
    
    lv_obj_t* getCard();
    bool handleButtonPress(uint8_t button_index) override;
    void prepareForRemoval() override;
    bool update() override;  // For game loop
    
private:
    FlappyBirdGame* game;
    lv_obj_t* cardContainer;
    bool markedForRemoval;
};

4. Update CardType Enum

In src/config/CardConfig.h:

  • Add new card type to enum
  • Update cardTypeToString() function
  • Update stringToCardType() function

5. Register the Card Type

In CardController::initializeCardTypes():

CardDefinition flappyDef;
flappyDef.type = CardType::FLAPPY_HOG;
flappyDef.name = "Flappy Hog";
flappyDef.allowMultiple = false;
flappyDef.needsConfigInput = false;
flappyDef.uiDescription = "One button. Endless frustration. Infinite glory.";
flappyDef.factory = [this](const String& configValue) -> lv_obj_t* {
    FlappyHogCard* newCard = new FlappyHogCard(screen);
    if (newCard && newCard->getCard()) {
        CardInstance instance{newCard, newCard->getCard()};
        dynamicCards[CardType::FLAPPY_HOG].push_back(instance);
        cardStack->registerInputHandler(newCard->getCard(), newCard);
        return newCard->getCard();
    }
    delete newCard;
    return nullptr;
};
registerCardType(flappyDef);

6. Handle Game Updates

For cards needing regular updates:

  1. Extend InputHandler interface with virtual bool update() { return false; }
  2. Add updateActiveCard() to CardNavigationStack
  3. Call it from CardController::processUIQueue()

This keeps the game loop encapsulated and running at the same frequency as UI updates.

7. Remove Old Integration Code

Clean up:

  • Direct references in main.cpp
  • Card-specific methods in CardController
  • Manual card creation in initialization

8. Test the Integration

Verify:

  • Card appears in web UI
  • Can be added/removed dynamically
  • Game functions properly
  • No memory leaks or crashes
  • Proper cleanup on removal

Common Pitfalls

  1. Threading issues - Keep all UI updates on the LVGL thread
  2. Memory management - Use prepareForRemoval() to prevent double-deletion
  3. Button handling - Return false from handleButtonPress() to allow navigation
  4. Update frequency - Game updates run at UI refresh rate (~60 FPS)

Benefits of the New System

  • Extensible - Easy to add new card types
  • Dynamic - Users control which cards are shown
  • Encapsulated - No card-specific code in core systems
  • Consistent - All cards follow the same pattern
  • Thread-safe - Updates happen through the UI queue

Merge Strategy

  1. Commit your changes on the feature branch
  2. Merge main into your branch (expect conflicts)
  3. Resolve conflicts keeping the new architecture
  4. Test thoroughly before creating PR

The key is to wrap the existing functionality in the new card system rather than trying to preserve the old integration approach. Do not accept changes that alter the architecture of the project as a whole.