# How the Main Electron Process Is Structured in escrcpy: A Modular Plugin Architecture

> Discover escrcpy's main Electron process structure. Explore its modular plugin architecture and how it wires environment setup, services, and UI modules via the mainApp.use() API.

- Repository: [viarotel-org/escrcpy](https://github.com/viarotel-org/escrcpy)
- Tags: internals
- Published: 2026-09-10

---

**The escrcpy main Electron process follows a declarative plugin system centered in [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js), where environment setup, system services, and UI modules are wired together via a `mainApp.use()` API that attaches functionality to the Electron application lifecycle.**

escrcpy provides a desktop interface for Android screen mirroring and control using Electron. The **main Electron process** implementation demonstrates a sophisticated modular design that separates runtime configuration, window management, and background operations into discrete, reusable units according to the source code in `viarotel-org/escrcpy`.

## Entry Point and Environment Initialization

The bootstrap sequence begins at [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js), which coordinates three distinct phases before the Electron app window appears.

First, the runtime environment is established by importing configuration modules:

```javascript
// Executed before Electron initialization
import './process/index.js'        // Sets RAW_PATH, IS_PACKAGED, DESKTOP_PATH, CWD
import './process/index.post.js'   // Post-process configuration adjustments

```

These environment variables populate `process.env` and determine asset paths, development mode detection, and working directories before any Electron APIs are invoked.

## Creating the Core Application

After environment setup, [`main.js`](https://github.com/viarotel-org/escrcpy/blob/main/main.js) invokes `createElectronApp` from the `@escrcpy/electron-setup` package to instantiate the application wrapper:

```javascript
import { createElectronApp } from '@escrcpy/electron-setup/main';

const mainApp = createElectronApp({
  preloadDir: __dirname,
  rendererDir: path.join(__dirname, '../dist'),
  devRendererDir: process.env.VITE_DEV_SERVER_URL,
  icon: getLogoPath(),
  width: browserWindowWidth,
  height: browserWindowHeight,
  backgroundColor: getAppBackgroundColor(),
});

```

This factory function returns a `mainApp` instance that exposes a `.use()` method for registering plugins, services, and modules declaratively.

## The Plugin Registration Architecture

Every service and module in escrcpy exports a **plugin-like object** adhering to a strict contract:

- **name**: A string identifier for debugging
- **apply(mainApp)**: An async function that receives the app instance and attaches logic

The wiring in [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js) registers these components in dependency order:

```javascript
// Core plugins from @escrcpy/electron-setup
mainApp.use(themePlugin);
mainApp.use(windowIPCPlugin);
mainApp.use(clipboardPlugin);

// Application services
mainApp.use(lifecycleService);
mainApp.use(edgerService);
mainApp.use(trayService);
mainApp.use(shortcutsService);

// UI window modules
mainApp.use(controlModule);
mainApp.use(explorerModule);
mainApp.use(terminalModule);

// Launch when Electron is ready
app.whenReady().then(() => mainApp.start());

```

This pattern ensures that tray icons, global shortcuts, and lifecycle hooks are established before UI windows attempt to render.

## Services vs. Modules: Architectural Separation

The codebase distinguishes between **Services** and **Modules** to maintain separation of concerns.

**Services** handle application-wide concerns without rendering UI. Located in `desktop/electron/services/`, they include:

- **trayService**: Manages the system tray icon and context menu
- **shortcutsService**: Registers global keyboard accelerators
- **lifecycleService**: Handles `before-quit`, `window-all-closed`, and cleanup logic
- **edgerService**: Manages edge-case handling and error recovery

**Modules** represent self-contained UI windows. Stored in `desktop/electron/modules/`, each exposes a `window` property (Vite/Vue entry point) and a `service` property that wires renderer-side IPC. The four primary modules are Control, Explorer, Terminal, and Main windows.

## Process Management and Debugging

Child processes spawned by escrcpy (scrcpy instances, ADB commands) are tracked via [`desktop/electron/process/manager.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/process/manager.js). This **Process Manager** maintains a registry of spawned subprocesses and provides cleanup methods to terminate them gracefully when the application exits.

For development, optional debugging utilities reside in `desktop/electron/helpers/debugger/`, which attach to both the main process and renderer contexts.

## Summary

- The **entry point** [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js) orchestrates the entire main Electron process through a sequential bootstrap of environment → core app → plugins → services → modules → start.
- **Environment variables** are initialized first via [`desktop/electron/process/index.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/process/index.js) before Electron APIs are available.
- The **plugin architecture** uses a `{ name, apply(mainApp) }` contract registered via `mainApp.use()` to keep the codebase modular and testable.
- **Services** manage global state (tray, shortcuts, lifecycle) while **Modules** encapsulate distinct UI windows (Control, Explorer, Terminal).
- **Process Manager** in [`desktop/electron/process/manager.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/process/manager.js) tracks child processes for clean shutdown.

## Frequently Asked Questions

### What is the entry point for the escrcpy main process?

The entry point is [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js). This file first imports environment configuration from [`desktop/electron/process/index.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/process/index.js), then creates the Electron app via `createElectronApp`, registers all services and modules, and finally calls `mainApp.start()` when Electron emits the `ready` event.

### How does escrcpy handle child processes like scrcpy and ADB?

Child processes are managed by [`desktop/electron/process/manager.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/process/manager.js), which maintains an internal registry of spawned processes. When the application receives a shutdown signal or the user triggers cleanup, the Process Manager iterates through active subprocesses and terminates them to prevent orphaned scrcpy or ADB instances from consuming system resources.

### What is the difference between a Service and a Module in escrcpy?

A **Service** is an application-wide utility that does not render UI (such as tray icons or global shortcuts), while a **Module** represents a complete UI window with its own renderer process. Services are stored in `desktop/electron/services/` and handle cross-cutting concerns, whereas Modules live in `desktop/electron/modules/` and each exposes both a window configuration and service logic for that specific view.

### How are plugins registered in the main Electron process?

Plugins follow a standardized object pattern with `name` and `apply(mainApp)` properties. The `mainApp.use()` method accepts these objects and invokes their `apply` function, passing the app instance. This allows third-party extensions or internal features to attach to the Electron lifecycle without modifying the core [`main.js`](https://github.com/viarotel-org/escrcpy/blob/main/main.js) logic directly.