Skip to content

DevExtensions

Bashar Astifan edited this page Aug 22, 2025 · 31 revisions

๐Ÿงฉ ImmExtension

Download/Clone this template first then review the details below

ImMobile extension template (Click Here)


๐Ÿงพ Information()

void Information() override;

Defines metadata for the extension:

  • name: The name of the extension (e.g., "ImmExt")
  • publisher: The developer's name (e.g., "Astifan")
  • version: Version string (e.g., "0.2")
  • build: Minimum OS build required (format: major.minor.build.revision)

There is special variable called unloadme, when you set this to true the extension will be auto unloaded

๐Ÿ”„ Update Support

To support updating your extension from a remote server:

Imm::App::Extension::Update(url, suggestedName);
  • Downloads and schedules the update for next startup
  • If the response doesnโ€™t provide a filename, the suggestedName will be used
  • Do not include .dll in the suggestedName

ImMobile provide direct way to grab and download files/extensions

just use imma:https://.../extName.dll then a popup will appear to the user

URI call imma: can be used in the browser directly

ImMobile will auto start as well if it was closed


โš ๏ธ GPU Cache

Starting from 1.2, ImMobile enabled by default to skip duplicated frames

this may impact your realtime drawing if you're updating image as example

there are many ways to trick the cache and force update like

void ForceImGuiVertexUpdate() {
	float t = ImGui::GetTime();
	ImU32 color = ImGui::GetColorU32(ImVec4(1, 1, 1, 1));
	color ^= (static_cast<int>(t * 1000) & 0xFF);

	ImDrawList* drawList = ImGui::GetWindowDrawList();
	ImVec2 p = ImGui::GetCursorScreenPos();
	drawList->AddRect(p, ImVec2(p.x + 1, p.y + 1), color);
}

๐Ÿงช Initialize()

void Initialize(ImmApiProvider* apiProvider) override;
  • Called after ImMobile sets up internal fields.
  • Use Imm:: namespace to access ImMobile's apiProvider functions.

โš™๏ธ Config()

void Config() override;
  • Called after Initialize().
  • Sets up internal configuration and GUI flags.
  • Recommended flags:
    flags = ImGuiWindowFlags_HorizontalScrollbar 
          | ImGuiWindowFlags_MenuBar 
          | ImGuiWindowFlags_NoCollapse 
          | ImGuiWindowFlags_SleepFPS 
          | ImGuiWindowFlags_30FPS;

๐Ÿงพ Register()

void Register() override;

Use this to register:

  • File types:

    Imm::App::Types::RegisterFileType({".xyz"}, "Description", [](std::string path) {
        Imm::Notify::Success("File received: " + path);
    });
  • Background intervals:

    Imm::Async::RegisterInterval("Label", INTERVAL_ONCE, []() {
        // Called in background
    });

This is the most safe in between phase that you can use to initial some stuff you want as well


โž• Addons()

void Addons() override;

Register UI addon icons with action handlers:

Imm::App::Extension::RegisterAddonItem("ImmExt", ICON_FA_WRENCH, [&]() {
    ToggleGUI();
});

๐ŸชŸ ToggleGUI()

void ToggleGUI() override;

Toggles your extension window's visibility using:

Imm::ImGuiEx::Window::FlipState(wd, &visibility);

๐ŸŽจ Render()

void Render() override;

This is where your GUI content should be drawn.

Use the recommended window wrapper:

if (Imm::ImGuiEx::Window::Begin(flags)) {
    Imm::Debug::DrawTestContent(flags, GetWindowID());
}
Imm::ImGuiEx::Window::End();

you can also register your free draw override

this preffered to be made at Register not inside render loop

helpful to start draw someting once the extension loaded

apiFunctions.ImDrawOverrideImm(createdExtension, static_cast<int>(ImMobileOverrides::IMM_OVERRIDE_FREEDRAW), [&]() {
  // Standalone Preview
  if (standalonePreview) {
    auto pflags = ImGuiWindowFlags_AlwaysAutoResize |
      ImGuiWindowFlags_NoNav |
      ImGuiWindowFlags_NoBackground |
      ImGuiWindowFlags_NoCollapse |
      ImGuiWindowFlags_NoTitleBar;
    if (ImGui::Begin("Lottie Preview###lottie_preview", & standalonePreview, pflags)) {
      if (standaloneBringToFront) {
        ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
      }

      float size = iconSize * Imm::Screen::Scale();
      LottiePreview(size);
      ImGui::End();
    }
  }
});

๐ŸŽฎ Input & System Events

Sensor Events

void Sensors(SensorsDirection direction, bool state) override;

TouchPad

void TouchPad(int port, int code, bool pressed) override;
void TouchPadAnalog(int port, float xAxis, float yAxis) override;

GamePad

void GamePad(int port, int code, bool pressed) override;
void GamePadAnalog(int port, float xAxisR, float yAxisR, float xAxisL, float yAxisL) override;
void GamePadConnected(int port) override;
void GamePadRemoved(int port) override;

System State

void Rotated(DXGI_MODE_ROTATION orientation) override;
void NetworkChanged(NetworkLevel level, bool wifi) override;
void EnergySaverChanged(SaverStatus status) override;
void BluetoothChanged(BluetoothState state) override;

๐Ÿ–ฑ๏ธ Pointer Events

These are optional unless you're handling custom UI without ImGui:

void PointerPressed(int id, float x, float y, ImPointerType type) override;
void PointerMoved(int id, float x, float y, ImPointerType type) override;
void PointerReleased(int id, float x, float y, ImPointerType type) override;

Input characters don't have custom event in the extension's events

but you can easily capture that via ImGui while rendering

ImGuiIO& io = ImGui::GetIO();
static bool previousKeys[512] = { false };

for (int key = 0; key < IM_ARRAYSIZE(io.KeysDown); ++key) {
        // This condition to avoid sending the same key multiple times
	if (io.KeysDown[key] != previousKeys[key]) {
		
	}
}

๐Ÿš€ URI Launch

void LaunchURI(std::string uri) override;

Handles custom launch schemes like:

imm:MyValue โ†’ uri = "MyValue"

โŒ Unload

void Unloading() override;

Called before the extension is unloaded. Clean up your memory, threads, and UI handles here.


๐Ÿงผ Destructor

~ImmExtension() override;

๐Ÿงช Testing & Examples

For practical usage examples and API tests, see the included file:

ImmExtenTemplate.cpp

This template demonstrates how to use the available APIs with real use cases.


API Providers

You cannot include ImmApiProvider.h in your .cpp files directly

as solution I have provided some helpful APIs, storage and log ones specifically at ImmApiProviderBridge.h

the above bridge for CPP, for C files use ImmApiProviderBridgeC.h

you can include this file and access to the APIs, if you want more you can simply expose more.


๐Ÿงฉ ImMobile Extensions APIs

This documentation provides an overview of the available API functions in the Imm namespace used by ImMobile extensions.

Below you'll find categorized descriptions of function signatures and their expected parameters.


New In 1.1

/* CMD */
bool RunCMD(std::string command, bool detached);
std::string RunCMD(std::string command, bool& state, bool detached);

/* INPUT */
bool IsInputPanVisible();

/* HASHING */
std::string Base64Encode(const std::string& in);
std::string Base64Decode(const std::string& in);
std::vector<uint8_t> ComputeSHA256BCrypt(const std::wstring& input);

/* UI */
float GetGlobalProgress();
void ToggleConfigSettingsWindow(const char* windowName, const char* configSection);
bool* GetWindowsVisibiltyPointer(int type); // Related to ImMobileOverrides, nullptr if not supported

/* EXTRA OPTIONS */
void ShowExtraOptions(std::string section);
void AppendExtraOptionsItem(std::string section, std::string item, std::function<void()> onClick);
void AppendExtraOptionsSpr(std::string section);

/* FILES */
void OpenFileWithSupportedApps(std::string file);
void OpenFilesBrowser(); // General use (default files browser behavior)

/* WIN32 / STD */
int fstatUWP(const char* name, struct stat* out); // Resolved internally for UWP access
int removeUWP(const void* name);
int renameUWP(char const* oldFileName, char const* newFileName);

/* IMGUI */
void SaveImGuiConfigs(); // Force direct save, ImMobile only save each 30 second

/* TOUCH */
void KeepInputPaneOpened(); // Use it on demand to keep touch keyboard opened (don't call it constantly)

/* ARCHIVE */
// Helpful when you have object not path (has task by default)
void zipStorageFolder(winrt::Windows::Storage::StorageFolder folder, std::string outputZipPath, std::function<void(bool, std::string)> callback);

Access those directly not through Imm:: provider.


๐Ÿ“‹ Logger

Imm::Logger::Normal(std::string text);
Imm::Logger::Warn(std::string text);
Imm::Logger::Error(std::string text);
Imm::Logger::Notice(std::string text);
Imm::Logger::Success(std::string text);

Logs messages to the ImMobile extension logger.


๐Ÿ’พ Storage

It's very important to replace your file stream functions with ImMobile API

ImMobile provide the exact call of the original ones, but they are internally resolved for UWP access

As mentioned you can access them through the bridge file ImmApiProviderBridge.h or ImmApiProviderBridgeC.h

๐Ÿ“‚ Pickers

// Choose file
Imm::Storage::Pickers::ChooseFile(callback, filters = ".*", multiple = false);

// Example
std::string filters = "Image{.jpg,.png,.jpeg},Text{.txt,.ini,.xml},.*";
Imm::Storage::Pickers::ChooseFile([&](const std::vector<PathUWP> files) {
  std::string path = files[0].ToString();
  Imm::Logger::Normal("Selected file:\n" + path);
}, filters);

// Save file
Imm::Storage::Pickers::SaveFile(callback, suggestedName = "");

// Example
std::string suggestedName = "test.txt";
Imm::Storage::Pickers::SaveFile([&](std::string path) {
  Imm::Logger::Normal("File to save:\n" + path);
}, suggestedName);

// Choose folder
Imm::Storage::Pickers::ChooseFolder(callback, multiple = false);

// Example
Imm::Storage::Pickers::ChooseFolder([&](const std::vector<PathUWP> folders) {
  std::string path = folders[0].ToString();
  Imm::Logger::Normal("Selected folder:\n" + path);
});

File and folder selection dialogs.

๐Ÿ“ Locations

Imm::Storage::Locations::InstallationFolder();
Imm::Storage::Locations::InternalIcons(icon);
Imm::Storage::Locations::LocalFolder();
Imm::Storage::Locations::TempFolder();
Imm::Storage::Locations::DataFolder();
Imm::Storage::Locations::MusicFolder();
Imm::Storage::Locations::VideosFolder();
Imm::Storage::Locations::PicturesFolder();
Imm::Storage::Locations::BackupFolder();
Imm::Storage::Locations::ExtensionsFolder();
Imm::Storage::Locations::RuntimesFolder();
Imm::Storage::Locations::DownloadsFolder();
Imm::Storage::Locations::TexturesFolder();
Imm::Storage::Locations::UpdatesFolder();
Imm::Storage::Locations::StartupFolder();
Imm::Storage::Locations::FontsFolder();
Imm::Storage::Locations::TempFilesFolder();
Imm::Storage::Locations::EnvironmentVariables(path);
Imm::Storage::Locations::GetTempFile(name);

Access to system and app-specific folders.

๐Ÿ› ๏ธ Manage

Imm::Storage::Manage::CreateFile(path, accessMode, shareMode, openMode);
Imm::Storage::Manage::CreateFolder(path, replaceExisting = true, skipIfExists = false);
Imm::Storage::Manage::GetFolderContents(path, deepScan = false);
Imm::Storage::Manage::IsExists(path);
Imm::Storage::Manage::IsDirectory(path);
Imm::Storage::Manage::GetSize(path);
Imm::Storage::Manage::Delete(path);
Imm::Storage::Manage::Rename(path, name);
Imm::Storage::Manage::Copy(path, dest, *cancelled = nullptr, *progress = nullptr);
Imm::Storage::Manage::Move(path, dest, *cancelled = nullptr, *progress = nullptr);
Imm::Storage::Manage::CheckPathAccess(path);
Imm::Storage::Manage::CheckDriveAccess(driveName, checkFutureAccess);
Imm::Storage::Manage::GetDriveFreeSpace(path, &space);

Manage files and directories.

for Copy and Move (cancelled, progress) can be obtained when you use it inside task


๐Ÿงต Stream

Custom file stream API wrappers:

Imm::Storage::Stream::FileGetStream(path, mode);
Imm::Storage::Stream::FileGetContents(&state, path, mode = "r+", reportIfNotExists = true);
Imm::Storage::Stream::FilePutContents(path, content, backup = false, createnew = false);
Imm::Storage::Stream::FileGetBuffer(path, &outSize);

C-style file operations:

Includes fopen, fread, fwrite, fseek, fclose, feof, ftell, and moreโ€”all wrapped internally.

It's very important to replace your file stream functions with ImMobile API

ImMobile provide the exact call of the original ones, but they are internally resolved for UWP access

As mentioned you can access them through the bridge file ImmApiProviderBridge.h or ImmApiProviderBridgeC.h


๐Ÿ—„๏ธ Archives

Imm::Archives::Extract(archive, dest, callback);   // Extracts archive to destination
Imm::Archives::Compress(folder, archive, callback); // Compresses folder into archive

๐Ÿงฉ App

๐Ÿ“ Localization

Imm::App::Localization::GetLocalString(data);

๐Ÿ–ผ๏ธ UI

Imm::App::UI::SetFullModeState(state);
Imm::App::UI::SetGameModeState(state);
Imm::App::UI::ProgressShow(percentage);
Imm::App::UI::ProgressHide();
Imm::App::UI::InvokeGoBack();
Imm::App::UI::CustomBackground(path, scale);

๐Ÿ“ Types

Imm::App::Types::RegisterFileType(extensions, title, onOpen);
Imm::App::Types::GetTypesList(type);

๐Ÿท๏ธ Version

Imm::App::Version::BuildString();
Imm::App::Version::BuildInfo(major, minor, build, revision);

๐Ÿงช Extension

Imm::App::Extension::InvokeCheckUpdates();
Imm::App::Extension::Update(url, suggestedName);
Imm::App::Extension::RegisterAddonItem(label, icon, onClick);

๐ŸŽจ Overrides

Imm::App::Overrides::TopBarDrawOverride(draw);
Imm::App::Overrides::BottomBarDrawOverride(draw);
Imm::App::Overrides::BackgroundDrawOverride(draw);
Imm::App::Overrides::HomeGridDrawOverride(draw);
Imm::App::Overrides::TouchPadDrawOverride(draw);

๐Ÿ”” Notify

Imm::Notify::Info(text, duration);
Imm::Notify::Success(text, duration);
Imm::Notify::Warn(text, duration);
Imm::Notify::Error(text, duration);

๐Ÿ“‹ Clipboard

Imm::Clipboard::Read();
Imm::Clipboard::Set(data);

๐Ÿ“‚ Launch

Imm::Launch::URI(uri);
Imm::Launch::File(file);
Imm::Launch::Folder(folder);

๐ŸŽฎ DirectX

๐Ÿ–ผ๏ธ Texture

Imm::DirectX::Texture::RequestCached(path, maxSize, outInfo, preferThumbnail, thumbnailMode);
Imm::DirectX::Texture::RequestCachedByURL(url, maxSize, outInfo, preferThumbnail, thumbnailMode);
Imm::DirectX::Texture::LoadFromFile(filename, maxSize, out_srv, out_width, out_height);
Imm::DirectX::Texture::GetThumbnail(path, outTextureView, out_width, out_height, mode);
Imm::DirectX::Texture::ReleaseTexture(texture);
Imm::DirectX::Texture::ImageSize(path, out_width, out_height);
Imm::DirectX::Texture::LoadDecodedBitmap(path, maxSize, outTextureView, out_width, out_height, format, alpha);

Cached images handled by cache life cycle and will auto cleanup, you don't have to do anything

Monitor textures cache from Options -> Lab -> Monitors

๐Ÿงฑ Context

Imm::DirectX::Context::D3DDevice();
Imm::DirectX::Context::D3DContext();
Imm::DirectX::Context::RenderTargetView();
Imm::DirectX::Context::SwapChain();
Imm::DirectX::Context::Texture2D();

๐Ÿง™ Shader

Imm::DirectX::Shader::CompileFromFile(szFileName, szEntryPoint, szShaderModel, ppBlobOut);
Imm::DirectX::Shader::LoadCompiledFromFile(filename);
Imm::DirectX::Shader::Level();
Imm::DirectX::Shader::VertexShaderModel();
Imm::DirectX::Shader::PixelShaderModel();
Imm::DirectX::Shader::LevelString();

๐Ÿงฐ ImGuiEx

๐ŸชŸ Window

// ๐Ÿ”ฒ Begin and end ImMobile window container
Imm::ImGuiEx::Window::Begin(flags);
Imm::ImGuiEx::Window::End();

// ๐Ÿ“ File browser state
Imm::ImGuiEx::Window::IsFileBrowserOpened();

// ๐Ÿ”„ Toggle or close specific windows by name
Imm::ImGuiEx::Window::FlipState(name, target_state_bool);
Imm::ImGuiEx::Window::CloseByName(name, afterClose);

// ๐Ÿ“‰ Check if minimized to avoid unwanted toggles
Imm::ImGuiEx::Window::IsMinimized(name);

// ๐Ÿ› ๏ธ Add layout and flags options to menus
Imm::ImGuiEx::Window::AddLayoutMenuItems(flags);
Imm::ImGuiEx::Window::DrawWindowMenuBar(target_pointer, flags);

// ๐Ÿ”™ Close window on back press (mobile UX)
Imm::ImGuiEx::Window::CheckBackPressedAndClose(toggle);

๐ŸŽ›๏ธ Elements

// ๐Ÿ”ค Centered text block, useful for splash messages or UIs
Imm::ImGuiEx::Elements::TextCentered(text, offsetx, offsety);

// ๐Ÿ”ก InputText with edit popup and custom keyboard support
Imm::ImGuiEx::Elements::InputText(label, buffer, size, flags, keyboard);

// ๐Ÿ–ผ๏ธ Load and render cached images with thumbnail fallback
Imm::ImGuiEx::Elements::Image(path, outInfo, renderSize, maxSize, preferThumbnail, thumbnailMode);

// ๐Ÿงฟ Render internal ImMobile icon by name
Imm::ImGuiEx::Elements::Icon(icon, outInfo, renderSize, maxSize, preferThumbnail, thumbnailMode);

// โšก Simplified Icon overload for quick use
Imm::ImGuiEx::Elements::Icon(icon, renderSize, maxSize, preferThumbnail, thumbnailMode);

// ๐ŸŒ Auto-download and render image by URL
Imm::ImGuiEx::Elements::ImageByURL(url, outInfo, renderSize, maxSize, preferThumbnail, thumbnailMode);

// ๐Ÿ”” Custom notification bar with icon and optional click
Imm::ImGuiEx::Elements::Notice(text, icon, backColor, padding, onClick);

๐Ÿงญ Position

// ๐Ÿ“ Horizontal, vertical or full centering
Imm::ImGuiEx::Position::CenterItemX(width, offset);
Imm::ImGuiEx::Position::CenterItemY(height, offset);
Imm::ImGuiEx::Position::CenterItem(width, height, offsetx, offsety);

๐Ÿ‘† Pointer

๐Ÿ“ Position & Coordinates

Imm::Pointer::PointerPos();         // Get full pointer/touch position (ImVec2)
Imm::Pointer::PointerX();           // Pointer X coordinate only
Imm::Pointer::PointerY();           // Pointer Y coordinate only

โœจ Touch & Gesture Detection

Imm::Pointer::IsPointReleased();          // True if pointer/touch was just released
Imm::Pointer::IsOnePointDetected();       // Single-touch detected (tap/drag)
Imm::Pointer::IsTwoPointsDetected();      // Multi-touch (two fingers) detected
Imm::Pointer::IsZoomBehaviorDetected();   // Zoom gesture behavior detected (pinch)
Imm::Pointer::IsDoubleClickBehaviorDetected(); // Double-tap detected (resets after call)

๐Ÿ› ๏ธ Behavior Controls

Imm::Pointer::SetScrollResolverState(state); // Control scroll/touch resolution (e.g., ImPlot zooming)
Imm::Pointer::IgnoreTouchEvent(ignoreReleaseState, ignoreUICheck); // Suppress UI or release-based touch handling

๐Ÿงฎ Hit Testing Helpers

Imm::Pointer::IsPointInButton(point, buttonPos, buttonSize);  // Check if point is inside a rectangle (e.g., UI button)
Imm::Pointer::IsPointInCircle(point, center, radius);         // Check if point is inside a circle (e.g., joystick/touchpad)

๐Ÿ“ฆ Packages

Imm::Packages::Install(package);
Imm::Packages::Register(manifest);
Imm::Packages::Remove(id);

Install, register, and remove Windows App packages.


๐Ÿ’ฌ Dialogs

Imm::Dialogs::Input(title, text, onClose, inputRequired = false);
Imm::Dialogs::Input(title, text, value, onClose, inputRequired = false);
Imm::Dialogs::Notice(title, text, onDontShowAgain, onClose);
Imm::Dialogs::Confirm(title, message, callback);
Imm::Dialogs::Confirm(title, drawFn, callback);

Display confirmation, input, and notice dialogs.


๐Ÿ–ฅ๏ธ Device Info

OS

Imm::Device::OS::Notification(title, message);
Imm::Device::OS::BuildString();
Imm::Device::OS::BuildInfo(&major, &minor, &build, &revision);

Hardware

Imm::Device::Hardware::BatteryLevel();
Imm::Device::Hardware::TotalRAM();
Imm::Device::Hardware::AvailableRAM();
Imm::Device::Hardware::BluetoothState();
Imm::Device::Hardware::BatterySaverState();
Imm::Device::Hardware::WiFiState();
Imm::Device::Hardware::ToggleBluetooth(state);

Type

Imm::Device::Type::IsMobile();
Imm::Device::Type::IsXBox();
Imm::Device::Type::DeviceFamily();

๐ŸŒ Online Tools

Imm::Online::IsOnline();
Imm::Online::GetFileName(url, defaultName, headers);
Imm::Online::GetResponseHeader(url, defaultName, headers, accept, notifications);
Imm::Online::QuickDownload(url, file, fullPath, httpClient, showNotifications, headers);
Imm::Online::GetResponse(url, accept, headers, notifyError);
Imm::Online::DownloadResponse(url, filePath, accept, headers, notifyError);
Imm::Online::GetDefaultDownloadClient();

๐Ÿ“ฆ GitHub Integration

Imm::GitHub::GetContents(user, repo, subPath, &output, accept, headers);
Imm::GitHub::GetLatestRelease(user, repo, &output, accept, headers);
Imm::GitHub::ShowReleasesUI(url, token);
Imm::GitHub::ShowContentsUI(url, subPath, token);
Imm::GitHub::ShowReadMeUI(url, token);

โœ๏ธ JSRuntime API

Imm::JSRuntime::LoadJSFile(file, callback, window = false, confirmation = false);
Imm::JSRuntime::Execute(jsID, script);
Imm::JSRuntime::Reset(jsID);
Imm::JSRuntime::Reload(jsID);
Imm::JSRuntime::Remove(jsID);
Imm::JSRuntime::IsLoaded(jsID);

๐Ÿ” Async & Interval

Imm::Async::WaitFor(&state);
Imm::Async::AddTask(title, type, callback);
Imm::Async::RegisterInterval(title, type, callback);

๐Ÿ”ง Utilities

Grouped under:

  • Imm::Utils::String
  • Imm::Utils::List
  • Imm::Utils::Colors
  • Imm::Utils::Time

Each includes common helper functions such as string manipulation, color conversions, formatting utilities, etc.


๐Ÿ–ฅ๏ธ Screen

๐Ÿ“ Dimensions & Scaling

Imm::Screen::Width();          // Screen width in pixels
Imm::Screen::Height();         // Screen height in pixels
Imm::Screen::Scale();          // UI scaling factor (useful for resolution-independent sizing)
Imm::Screen::FontScale();      // Font scaling based on DPI / user settings
Imm::Screen::DPI();            // Device DPI (dots per inch)

๐Ÿงฑ UI Layout Helpers

Imm::Screen::BottomBarSize();  // Height of the bottom navigation bar (if any)
Imm::Screen::TopBarSize();     // Height of the top bar (if any)
Imm::Screen::MinButtonWidth(); // Minimum recommended button width (responsive design helper)
Imm::Screen::IsLandScape();    // True if device is in landscape orientation

๐Ÿ”Š Audio

๐ŸŽต SoundTrack

Imm::Audio::SoundTrack::Play(path);              // Play audio file (streamed)
Imm::Audio::SoundTrack::Pause(path);             // Pause audio playback
Imm::Audio::SoundTrack::Resume(path);            // Resume paused audio
Imm::Audio::SoundTrack::Seek(path, seconds);     // Seek to time position
Imm::Audio::SoundTrack::Stop(path);              // Stop specific track
Imm::Audio::SoundTrack::StopAll();               // Stop all playing tracks
Imm::Audio::SoundTrack::GetCurrentPosition(path);// Get current playback position
Imm::Audio::SoundTrack::GetTotalDuration(path);  // Get total length of the track
Imm::Audio::SoundTrack::GetRemainingTime(path);  // Time left in track
Imm::Audio::SoundTrack::ClearCache(path);        // Clear cache for a specific track
Imm::Audio::SoundTrack::ClearCacheAll();         // Clear all audio caches

๐Ÿ’ฅ SoundEffect

Imm::Audio::SoundEffect::Play(sfx);              // Play built-in UI sound effect (default: click)
Imm::Audio::SoundEffect::Play(path);             // Play custom SFX by path
Imm::Audio::SoundEffect::Stop(path);             // Stop specific SFX
Imm::Audio::SoundEffect::StopAll();              // Stop all sound effects
Imm::Audio::SoundEffect::ClearCache(path);       // Clear cache for one SFX
Imm::Audio::SoundEffect::ClearCacheAll();        // Clear all SFX caches

๐Ÿ“‚ Config

Imm::Config::SaveString(key, value);
Imm::Config::GetString(key, def);

Imm::Config::SaveInt(key, value);
Imm::Config::GetInt(key, def);

Imm::Config::SaveBool(key, value);
Imm::Config::GetBool(key, def);

Imm::Config::SaveFloat(key, value);
Imm::Config::GetFloat(key, def);

Imm::Config::SaveDouble(key, value);
Imm::Config::GetDouble(key, def);

Imm::Config::SaveImVec2(key, value);
Imm::Config::GetImVec2(key, def);

Imm::Config::SaveImVec4(key, value);
Imm::Config::GetImVec4(key, def);

To avoid conflicts use unique names, ImMobile don't add any prefix to the key


๐Ÿ…ฐ๏ธ Font Icons

Many FA icons may appear as ?

this because ImMobile load only required set of icons to reduce memory usage

you need to import/rebuild the font if you want more

the list below is what loaded by ImMobile by default:

Click to expand full icon list
  • ICON_FA_POWER_OFF
  • ICON_FA_SPINNER
  • ICON_FA_SPINNER_THIRD
  • ICON_FA_INFO
  • ICON_FA_ARROW_TO_RIGHT
  • ICON_FA_COG
  • ICON_FA_MEMORY
  • ICON_FA_PALETTE
  • ICON_FA_FILE_PLUS
  • ICON_FA_FILE_EDIT
  • ICON_FA_FILE_CODE
  • ICON_FA_CALCULATOR
  • ICON_FA_CHART_AREA
  • ICON_FA_TASKS
  • ICON_FA_FILE_CHECK
  • ICON_FA_DRAW_POLYGON
  • ICON_FA_FONT
  • ICON_FA_GLOBE
  • ICON_FA_ARROW_LEFT
  • ICON_FA_TV
  • ICON_FA_LINK
  • ICON_FA_ARROW_DOWN
  • ICON_FA_ARROW_UP
  • ICON_FA_PASTE
  • ICON_FA_BROOM
  • ICON_FA_CHECK
  • ICON_FA_ARROW_RIGHT
  • ICON_FA_DICE_ONE
  • ICON_FA_DICE_THREE
  • ICON_FA_DICE_TWO
  • ICON_FA_DICE_FOUR
  • ICON_FA_DICE_FIVE
  • ICON_FA_DICE_SIX
  • ICON_FA_PLAY
  • ICON_FA_STOP
  • ICON_FA_BARS
  • ICON_FA_CHECK_CIRCLE
  • ICON_FA_EXCLAMATION_TRIANGLE
  • ICON_FA_TIMES_CIRCLE
  • ICON_FA_INFO_CIRCLE
  • ICON_FA_TEXT
  • ICON_FA_IMAGE
  • ICON_FA_MUSIC
  • ICON_FA_VIDEO
  • ICON_FA_CODE
  • ICON_FA_TERMINAL
  • ICON_FA_CUBES
  • ICON_FA_DATABASE
  • ICON_FA_COMPACT_DISC
  • ICON_FA_WINDOW
  • ICON_FA_ARCHIVE
  • ICON_FA_FILE
  • ICON_FA_FOLDER
  • ICON_FA_QUESTION_SQUARE
  • ICON_FA_PI
  • ICON_FA_SQUARE_ROOT
  • ICON_FA_BACKSPACE
  • ICON_FA_GAMEPAD
  • ICON_FA_TOGGLE_ON
  • ICON_FA_CABINET_FILING
  • ICON_FA_PLUS
  • ICON_FA_TRASH
  • ICON_FA_SAVE
  • ICON_FA_UNDO
  • ICON_FA_BOLT
  • ICON_FA_BOOK
  • ICON_FA_BOOKS
  • ICON_FA_HEART
  • ICON_FA_TIMES
  • ICON_FA_WIFI
  • ICON_FA_COFFEE
  • ICON_FA_MOBILE_ANDROID
  • ICON_FA_MOBILE
  • ICON_FA_LIST
  • ICON_FA_LIST_ALT
  • ICON_FA_CLOUD
  • ICON_FA_WAVE_SINE
  • ICON_FA_BATTERY_EMPTY
  • ICON_FA_BATTERY_SLASH
  • ICON_FA_BATTERY_BOLT
  • ICON_FA_BLUETOOTH
  • ICON_FA_MAP_MARKER_MINUS
  • ICON_FA_MAP_MARKER_PLUS
  • ICON_FA_ARROW_TO_BOTTOM
  • ICON_FA_EYE
  • ICON_FA_REDO
  • ICON_FA_BACKWARD
  • ICON_FA_FORWARD
  • ICON_FA_DOWNLOAD
  • ICON_FA_COGS
  • ICON_FA_COPY
  • ICON_FA_STAR
  • ICON_FA_KEYBOARD
  • ICON_FA_KEY
  • ICON_FA_MOUSE_POINTER
  • ICON_FA_MOUSE
  • ICON_FA_HAND_POINTER
  • ICON_FA_SEARCH
  • ICON_FA_EYE_SLASH
  • ICON_FA_HDD
  • ICON_FA_DIAMOND
  • ICON_FA_TEXT_SIZE
  • ICON_FA_TEXT_HEIGHT
  • ICON_FA_TH
  • ICON_FA_ELLIPSIS_H
  • ICON_FA_ELLIPSIS_V
  • ICON_FA_LOCK
  • ICON_FA_LOCK_OPEN
  • ICON_FA_WRENCH
  • ICON_FA_HEARTBEAT
  • ICON_FA_HEART_RATE
  • ICON_FA_HOME
  • ICON_FA_PLUG
  • ICON_FA_SHIELD_CHECK
  • ICON_FA_CERTIFICATE
  • ICON_FA_STARS
  • ICON_FA_LANDMARK
  • ICON_FA_STORE
  • ICON_FA_ROBOT

๐Ÿ–ฅ๏ธ Focus & Full Mode

There are many cases you want to toggle between the window mode to full mode

this is helpful for gaming and emulation as example, however some issues you may face

when you switch full mode state you may get your window in-active

you can fix that by enabling Render Always to trick the user to keep interacting

then once the user clicked to interact the window will get focus again..

below code basic sample on how to toggle between full & normal mode

Click to expand C++ code
bool hasFullScreen = true;
std::atomic<bool> gamingModeState = false;
std::atomic<bool> demandFocus = { false };
std::atomic<int> totalCalls = { 0 };
bool isFlagsReleaseInProgress = false;
bool isFlagsReStored = true;

void EnterFullScreen() {
	if (hasFullScreen) {
		Imm::App::UI::SetFullModeState(true);

		auto currentExt = static_cast<IExtension*>(createdExtension);

		if (!(currentExt->flags & ImGuiWindowFlags_RenderAlways)) {
			currentExt->flags |= ImGuiWindowFlags_RenderAlways;
		}
		if (!(currentExt->flags & ImGuiWindowFlags_RequireFull)) {
			currentExt->flags |= ImGuiWindowFlags_RequireFull;
		}
		if (!(currentExt->flags & ImGuiWindowFlags_NoTitleBar)) {
			currentExt->flags |= ImGuiWindowFlags_NoTitleBar;
		}
		if (!(currentExt->flags & ImGuiWindowFlags_NoMove)) {
			currentExt->flags |= ImGuiWindowFlags_NoMove;
		}
		if (currentExt->flags & ImGuiWindowFlags_MenuBar) {
			currentExt->flags &= ~ImGuiWindowFlags_MenuBar;
		}

		totalCalls.store(0);
		gamingModeState.store(true);
		demandFocus.store(true);

		ImGuiIO& io = ImGui::GetIO();
		if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) {
			io.ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard;
		}
	}
}
void ExitFullScreen() {
	if (hasFullScreen) {
		Imm::App::UI::SetFullModeState(false);

		auto currentExt = static_cast<IExtension*>(createdExtension);

		if (currentExt->flags & ImGuiWindowFlags_RenderAlways) {
			currentExt->flags &= ~ImGuiWindowFlags_RenderAlways;
		}
		if (currentExt->flags & ImGuiWindowFlags_RequireFull) {
			currentExt->flags &= ~ImGuiWindowFlags_RequireFull;
		}
		if (currentExt->flags & ImGuiWindowFlags_NoTitleBar) {
			currentExt->flags &= ~ImGuiWindowFlags_NoTitleBar;
		}
		if (currentExt->flags & ImGuiWindowFlags_NoMove) {
			currentExt->flags &= ~ImGuiWindowFlags_NoMove;
		}
		if (!(currentExt->flags & ImGuiWindowFlags_MenuBar)) {
			currentExt->flags |= ImGuiWindowFlags_MenuBar;
		}

		totalCalls.store(0);
		gamingModeState.store(false);
		demandFocus.store(true);

		// Add keyboard navigation flag back
		ImGuiIO& io = ImGui::GetIO();
		if (!(io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)) {
			io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
		}
	}
}
void Render() override {
	// Its better to build window with (Imm::ImGuiEx::Window)
	// this will allow ImMobile to apply its own windows behavior

	if (Imm::ImGuiEx::Window::Begin(flags)) {
		if (ImGui::IsWindowFocused()) {
			if (!isFlagsReleaseInProgress && !isFlagsReStored && flags & ImGuiWindowFlags_RenderAlways) {
				isFlagsReleaseInProgress = true;
				isFlagsReStored = true;
				concurrency::create_task([&] {
					std::this_thread::sleep_for(std::chrono::milliseconds(250));
					flags ^= ImGuiWindowFlags_RenderAlways;
					isFlagsReleaseInProgress = false;
					});
			}
		}

		if (demandFocus.load(std::memory_order_relaxed)) {
			auto window = ImGui::GetCurrentWindow();
			if (window) {
				ImGui::BringWindowToFocusFront(window);
			}
			totalCalls.fetch_add(1, std::memory_order_relaxed);

			ImGuiIO& io = ImGui::GetIO();
			ImVec2 screenSize = io.DisplaySize;

			if (gamingModeState.load(std::memory_order_relaxed)) {
				ImGui::SetWindowSize(screenSize, ImGuiCond_Always);
			}
			else {
				screenSize.y -= Imm::Screen::BottomBarSize();
				ImGui::SetWindowSize(screenSize, ImGuiCond_Always);
			}

			if (totalCalls.load(std::memory_order_relaxed) < 120) {
				ForceImGuiVertexUpdate();
			}
			else {
				demandFocus.store(false);
			}
		}

	}
	else {
		// Window is not active
		// to force always draw add `ImGuiWindowFlags_RenderAlways` to flags

		if (demandFocus.load(std::memory_order_relaxed)) {
			if (!(flags & ImGuiWindowFlags_RenderAlways)) {
				flags |= ImGuiWindowFlags_RenderAlways;
				isFlagsReStored = false;
			}
		}
	}
	Imm::ImGuiEx::Window::End();
}

๐Ÿงพ Startup & Home

By default ImMobile don't enable extension as startup nor for home grid

if you want to shorten that for the user make sure to enable that by code

below is how ImMobile handle that:

Windows 10 Template (Click)
// For Windows 10 Template
std::map<std::string, bool> gridItemsState;
void saveAddonsHomeGridState() {
	// Convert map to JSON
	nlohmann::json jsonObj = gridItemsState;

	std::string homeGridConfigs = Imm::Storage::Locations::DataFolder() + "\\configs\\addons.json";

	// Open file in write mode
	FILE* file = Imm::Storage::Stream::FileGetStream(homeGridConfigs, "w+");
	if (!file) {
		std::cerr << "Could not open file for writing\n";
		Imm::Logger::Error("Could not open file for writing (addons.json)\n");
		return;
	}

	// Write JSON to file
	Imm::Storage::Stream::fputs(jsonObj.dump().c_str(), file);

	// Close the file
	Imm::Storage::Stream::fclose(file);
}

void LoadAddonsHomeGridState() {
	std::string homeGridConfigs = Imm::Storage::Locations::DataFolder() + "\\configs\\addons.json";
	// Open file in read mode
	FILE* file = Imm::Storage::Stream::FileGetStream(homeGridConfigs, "r");
	if (!file) {
		std::cerr << "Could not open file for reading\n";
		Imm::Logger::Warn("Could not open file for reading (addons.json)");
		return;
	}

	// Read file content into a string
	std::string jsonString;
	char buffer[256];
	while (Imm::Storage::Stream::fgets(buffer, sizeof(buffer), file)) {
		jsonString += buffer;
	}

	// Close the file
	Imm::Storage::Stream::fclose(file);

	// Parse the JSON string back to a map
	nlohmann::json jsonObj = nlohmann::json::parse(jsonString);
	gridItemsState.clear();
	gridItemsState = jsonObj.get<std::map<std::string, bool>>();
}
Windows 8.1 Template (Click)
// For Windows 8.1 Template 

std::map<std::string, bool> gridItemsState;
void saveAddonsHomeGridState() {
	picojson::object jsonObj;

	// Manually insert map values
	for (const auto& pair : gridItemsState) {
		jsonObj[pair.first] = picojson::value(pair.second);
	}

	std::string homeGridConfigs = Imm::Storage::Locations::DataFolder() + "\\configs\\addons.json";

	FILE* file = Imm::Storage::Stream::FileGetStream(homeGridConfigs, "w+");
	if (!file) {
		std::cerr << "Could not open file for writing\n";
		Imm::Logger::Error("Could not open file for writing (addons.json)\n");
		return;
	}

	std::string jsonStr = picojson::value(jsonObj).serialize();
	Imm::Storage::Stream::fputs(jsonStr.c_str(), file);
	Imm::Storage::Stream::fclose(file);
}

void LoadAddonsHomeGridState() {
	std::string homeGridConfigs = Imm::Storage::Locations::DataFolder() + "\\configs\\addons.json";
	FILE* file = Imm::Storage::Stream::FileGetStream(homeGridConfigs, "r");
	if (!file) {
		std::cerr << "Could not open file for reading\n";
		Imm::Logger::Warn("Could not open file for reading (addons.json)");
		return;
	}

	std::string jsonString;
	char buffer[256];
	while (Imm::Storage::Stream::fgets(buffer, sizeof(buffer), file)) {
		jsonString += buffer;
	}
	Imm::Storage::Stream::fclose(file);

	picojson::value jsonVal;
	std::string err = picojson::parse(jsonVal, jsonString);
	if (!err.empty()) {
		std::cerr << "JSON parse error: " << err << "\n";
		Imm::Logger::Error("JSON parse error in addons.json: " + err);
		return;
	}

	gridItemsState.clear();
	if (!jsonVal.is<picojson::object>()) {
		std::cerr << "Invalid JSON format\n";
		Imm::Logger::Error("Invalid JSON format in addons.json");
		return;
	}

	const picojson::object& jsonObj = jsonVal.get<picojson::object>();
	for (const auto& pair : jsonObj) {
		if (pair.second.is<bool>()) {
			gridItemsState[pair.first] = pair.second.get<bool>();
		}
	}
}

Now to enable home grid do this (at Register phase)

// HomeGrid
LoadAddonsHomeGridState();
if (gridItemsState.find(id) != gridItemsState.end()) {
	gridItemsState[id] = true;
}
else {
	gridItemsState.insert({ id , true });
}
saveAddonsHomeGridState();
gridItemsState.clear();
Imm::Logger::Warn("ImmExt HomeGrid Enabled");

for startup it's much simple for now (at Register phase)

// Startup
std::string startupPath = Imm::Storage::Locations::StartupFolder();
Imm::Storage::Stream::FilePutContents(startupPath + "\\ImmExtFile.dll.imms", "", false, true);
Imm::Logger::Warn("ImmExt Startup Enabled");

the file name (ImmExtFile.dll.imms) depends on your extension output name

To do that only once on first start perform those (at Register phase)

if (!Imm::Config::GetBool("myext_secondload", false)) {
	Imm::Config::SaveBool("myext_secondload", true);

	// Do stuff for first start only
}

More may available in the official template,

follow the same construct way I used to inspect them,

also more details added to the functions inside the template.

Clone this wiki locally