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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion companion/lib/Surface/Controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-check
/*
* This file is part of the Companion project
* Copyright (c) 2018 Bitfocus AS
Expand Down Expand Up @@ -41,6 +40,7 @@ import SurfaceIPVideohubPanel from './IP/VideohubPanel.js'
import FrameworkMacropadDriver from './USB/FrameworkMacropad.js'
import CoreBase from '../Core/Base.js'
import { SurfaceGroup } from './Group.js'
import { SurfaceLayoutRegistry } from './LayoutRegistry.js'

// Force it to load the hidraw driver just in case
HID.setDriverType('hidraw')
Expand All @@ -49,6 +49,13 @@ HID.devices()
const SurfacesRoom = 'surfaces'

class SurfaceController extends CoreBase {
/**
* @type {SurfaceLayoutRegistry}
* @access private
* @readonly
*/
#surfaceLayouts

/**
* The last sent json object
* @type {Record<string, ClientDevicesListItem> }
Expand Down Expand Up @@ -112,6 +119,8 @@ class SurfaceController extends CoreBase {
constructor(registry) {
super(registry, 'Surface/Controller')

this.#surfaceLayouts = new SurfaceLayoutRegistry()

this.#surfacesAllLocked = !!this.userconfig.getKey('link_lockouts')

setImmediate(() => {
Expand Down Expand Up @@ -422,6 +431,11 @@ class SurfaceController extends CoreBase {
for (let surface of this.#surfaceHandlers.values()) {
if (surface && surface.surfaceId == id) {
surface.setPanelConfig(config)

setImmediate(() => {
this.updateDevicesList()
})

return surface.getPanelConfig()
}
}
Expand Down Expand Up @@ -531,6 +545,10 @@ class SurfaceController extends CoreBase {

return group.groupConfig
})

client.onPromise('surfaces:get-layouts', () => {
return this.#surfaceLayouts.getLayouts()
})
}

/**
Expand Down Expand Up @@ -651,6 +669,21 @@ class SurfaceController extends CoreBase {
isConnected: !!surfaceHandler,
displayName: getSurfaceName(config, id),
location: null,
xOffset: config.config?.xOffset ?? 0,
yOffset: config.config?.yOffset ?? 0,
layout: config.layout ?? null,
}

// If the surface has a cached grid size, a crude layout can be generated
if (config.gridSize && !surfaceInfo.layout) {
surfaceInfo.layout = {
id: '__auto__',
name: surfaceInfo.displayName,

type: 'grid',
rows: config.gridSize.rows,
columns: config.gridSize.columns,
}
}

if (surfaceHandler) {
Expand All @@ -659,6 +692,15 @@ class SurfaceController extends CoreBase {

surfaceInfo.location = location || null
surfaceInfo.configFields = surfaceHandler.panel.info.configFields || []

surfaceInfo.layout = {
id: '__auto__',
name: surfaceInfo.displayName,

type: 'grid',
rows: surfaceHandler.panelGridSize.rows,
columns: surfaceHandler.panelGridSize.columns,
}
}

return surfaceInfo
Expand Down
194 changes: 194 additions & 0 deletions companion/lib/Surface/LayoutRegistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* This file is part of the Companion project
* Copyright (c) 2018 Bitfocus AS
* Authors: William Viker <william@bitfocus.io>, Håkon Nessjøen <haakon@bitfocus.io>
*
* This program is free software.
* You should have received a copy of the MIT licence as well as the Bitfocus
* Individual Contributor License Agreement for companion along with
* this program.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the Companion software without
* disclosing the source code of your own applications.
*
*/

import { PRODUCTS as XKeysProducts } from 'xkeys'
import { contourShuttleXpressInfo, contourShuttleProV1Info, contourShuttleProV2Info } from './USB/ContourShuttle.js'

export class SurfaceLayoutRegistry {
/**
* The list of known surface layouts
* @type {import("@companion-app/shared/Model/Surfaces.js").SurfaceLayoutSchema[]}
* @access private
* @readonly
*/
#layouts = []

constructor() {
this.#addCountourShuttleLayouts()
this.#addStreamdeckLayouts()
this.#addLoupedeckLayouts()
this.#addInfinittonLayouts()
this.#addVideohubLayouts()
this.#addXKeysLayouts()

// Sort by name
this.#layouts.sort((a, b) => a.name.localeCompare(b.name))
}

#addCountourShuttleLayouts() {
this.#layouts.push(
{
id: 'contour-shuttle-xpress',
name: 'Contour Shuttle Xpress',
type: 'grid',
rows: contourShuttleXpressInfo.totalRows,
columns: contourShuttleXpressInfo.totalCols,
},
{
id: 'contour-shuttle-pro-v1',
name: 'Contour Shuttle Pro v1',
type: 'grid',
rows: contourShuttleProV1Info.totalRows,
columns: contourShuttleProV1Info.totalCols,
},
{
id: 'contour-shuttle-pro-v2',
name: 'Contour Shuttle Pro v2',
type: 'grid',
rows: contourShuttleProV2Info.totalRows,
columns: contourShuttleProV2Info.totalCols,
}
)
}

#addStreamdeckLayouts() {
this.#layouts.push(
{
id: 'streamdeck-15',
name: 'Elgato Streamdeck Original',
type: 'grid',
rows: 3,
columns: 5,
},
{
id: 'streamdeck-xl',
name: 'Elgato Streamdeck XL',
type: 'grid',
rows: 4,
columns: 8,
},
{
id: 'streamdeck-mini',
name: 'Elgato Streamdeck Mini',
type: 'grid',
rows: 2,
columns: 3,
},
{
id: 'streamdeck-plus',
name: 'Elgato Streamdeck +',
type: 'grid',
rows: 4,
columns: 4,
},
{
id: 'streamdeck-pedal',
name: 'Elgato Streamdeck Pedal',
type: 'grid',
rows: 1,
columns: 3,
},
{
id: 'streamdeck-neo',
name: 'Elgato Streamdeck Neo',
type: 'grid',
rows: 3,
columns: 4,
}
)
}

#addLoupedeckLayouts() {
this.#layouts.push(
{
id: 'loupedeck-live',
name: 'Loupedeck Live',
type: 'grid',
rows: 4,
columns: 8,
},
{
id: 'loupedeck-live-s',
name: 'Loupedeck Live S',
type: 'grid',
rows: 3,
columns: 7,
},
{
id: 'razer-stream-controller',
name: 'Razer Stream Controller',
type: 'grid',
rows: 4,
columns: 8,
},
{
id: 'razer-stream-controller-x',
name: 'Razer Stream Controller X',
type: 'grid',
rows: 3,
columns: 5,
},
{
id: 'loupedeck-ct',
name: 'Loupedeck CT',
type: 'grid',
rows: 8, // TODO verify
columns: 8,
}
)
}

#addInfinittonLayouts() {
this.#layouts.push({
id: 'infinitton-idisplay',
name: 'Infinitton idisplay',
type: 'grid',
rows: 3,
columns: 5,
})
}

#addVideohubLayouts() {
this.#layouts.push({
id: 'blackmagic-videohub-smart-control',
name: 'Videohub Smart Control',
type: 'grid',
rows: 2,
columns: 20,
})
}

#addXKeysLayouts() {
for (const [id, product] of Object.entries(XKeysProducts)) {
this.#layouts.push({
id: `xkeys-${id}`,
name: `XKeys ${product.name}`,
type: 'grid',

rows: product.rowCount,
columns: product.colCount,
})
}
}

/**
* @returns {import("@companion-app/shared/Model/Surfaces.js").SurfaceLayoutSchema[]}
*/
getLayouts() {
return this.#layouts
}
}
6 changes: 3 additions & 3 deletions companion/lib/Surface/USB/ContourShuttle.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import EventEmitter from 'events'
import shuttleControlUSB from 'shuttle-control-usb'
import LogController from '../../Log/Controller.js'

const contourShuttleXpressInfo = {
export const contourShuttleXpressInfo = {
// Treat as:
// 3 buttons
// button, two encoders (jog and shuttle), button
Expand All @@ -40,7 +40,7 @@ const contourShuttleXpressInfo = {
[3, 1],
],
}
const contourShuttleProV1Info = {
export const contourShuttleProV1Info = {
// Same as Pro V2 only without the buttons either side of the encoders
// Map the same for consistency and compatibility
totalCols: 5,
Expand Down Expand Up @@ -72,7 +72,7 @@ const contourShuttleProV1Info = {
[2, 3],
],
}
const contourShuttleProV2Info = {
export const contourShuttleProV2Info = {
// 4 buttons
// 5 buttons
// button, two encoders (jog and shuttle), button
Expand Down
33 changes: 33 additions & 0 deletions shared-lib/lib/Model/Surfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export interface ClientSurfaceItem {
isConnected: boolean
displayName: string
location: string | null

xOffset: number
yOffset: number
layout: SurfaceLayoutSchema | null
}

export interface ClientDevicesListItem {
Expand Down Expand Up @@ -46,3 +50,32 @@ export interface SurfacesUpdateUpdateOp {

patch: JsonPatchOperation[]
}

export type SurfaceLayoutSchema = SurfaceLayoutSchemaGrid | SurfaceLayoutSchemaComplex

export interface SurfaceLayoutSchemaBase {
id: string
name: string
}

export interface SurfaceLayoutSchemaGrid {
id: string
name: string

type: 'grid'

rows: number
columns: number
}

export interface SurfaceLayoutSchemaComplex {
id: string
name: string

type: 'complex'

/**
* Background image, recommended to be a svg when possible
*/
backgroundImage: string
}
9 changes: 8 additions & 1 deletion shared-lib/lib/SocketIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import type {
HelpDescription,
WrappedImage,
} from './Model/Common.js'
import type { ClientDevicesListItem, SurfaceGroupConfig, SurfacePanelConfig, SurfacesUpdate } from './Model/Surfaces.js'
import type {
ClientDevicesListItem,
SurfaceGroupConfig,
SurfaceLayoutSchema,
SurfacePanelConfig,
SurfacesUpdate,
} from './Model/Surfaces.js'
import type {
ClientImportObject,
ClientImportSelection,
Expand Down Expand Up @@ -223,6 +229,7 @@ export interface ClientToBackendEventsMap {
'surfaces:config-get': (surfaceId: string) => SurfacePanelConfig | null
'surfaces:config-set': (surfaceId: string, panelConfig: SurfacePanelConfig) => SurfacePanelConfig | string
'surfaces:group-config-get': (groupId: string) => SurfaceGroupConfig
'surfaces:get-layouts': () => SurfaceLayoutSchema[]

'emulator:startup': (emulatorId: string) => EmulatorConfig
'emulator:press': (emulatorId: string, column: number, row: number) => void
Expand Down
Loading