Change:
Currently events are handled in each terminal instance, this means that event handlers go off for each terminal instances for an event on the terminal panel.

Discussion:
import lifecycle = require('vs/base/common/lifecycle'); import nls = require('vs/nls'); import os = require('os'); <del> import platform = require('vs/base/common/platform'); </del> import xterm = require('xterm'); import {Dimension} from 'vs/base/browser/builder'; <del> import {IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding'; </del> <ins> import {IContextMenuService} from 'vs/platform/contextview/browser/contextView'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding'; </ins> import {IMessageService, Severity} from 'vs/platform/message/common/message'; import {ITerminalFont} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; import {ITerminalProcess, ITerminalService} from 'vs/workbench/parts/terminal/electron-browser/terminal';

public constructor( private terminalProcess: ITerminalProcess, private parentDomElement: HTMLElement, <ins> private contextMenuService: IContextMenuService, </ins> private contextService: IWorkspaceContextService, <ins> private instantiationService: IInstantiationService, private keybindingService: IKeybindingService, </ins> private terminalService: ITerminalService, private messageService: IMessageService, private terminalFocusContextKey: IKeybindingContextKey<boolean>,

this.onExitCallback(this); } }); <del> this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mousedown', (event) => { // Drop selection and focus terminal on Linux to enable middle button paste when click // occurs on the selection itself. if (event.which === 2 && platform.isLinux) { this.focus(true); } })); this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mouseup', (event) => { if (event.which !== 3) { this.focus(); } })); this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'keyup', (event: KeyboardEvent) => { // Keep terminal open on escape if (event.keyCode === 27) { event.stopPropagation(); } })); </del> this.xterm.open(this.terminalDomElement);

*--------------------------------------------------------------------------------------------*/ import DOM = require('vs/base/browser/dom'); <del> import {getBaseThemeId} from 'vs/platform/theme/common/themes'; </del> import lifecycle = require('vs/base/common/lifecycle'); <ins> import nls = require('vs/nls'); </ins> import platform = require('vs/base/common/platform'); import {Action, IAction} from 'vs/base/common/actions'; import {Builder, Dimension} from 'vs/base/browser/builder'; <del> import {KillTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem} from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; </del> <ins> import {getBaseThemeId} from 'vs/platform/theme/common/themes'; </ins> import {IActionItem} from 'vs/base/browser/ui/actionbar/actionbar'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; <del> import {IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding'; </del> <ins> import {IContextMenuService} from 'vs/platform/contextview/browser/contextView'; </ins> import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; <ins> import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybinding'; </ins> import {IMessageService} from 'vs/platform/message/common/message'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; <ins> import {ITerminalFont, TerminalConfigHelper} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; </ins> import {ITerminalProcess, ITerminalService, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/electron-browser/terminal'; import {IThemeService} from 'vs/workbench/services/themes/common/themeService'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; <ins> import {KillTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem, CopyTerminalSelectionAction, TerminalPasteAction} from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; </ins> import {Panel} from 'vs/workbench/browser/panel'; <del> import {TPromise} from 'vs/base/common/winjs.base'; import {ITerminalFont, TerminalConfigHelper} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; </del> <ins> import {Separator} from 'vs/base/browser/ui/actionbar/actionbar'; import {StandardMouseEvent} from 'vs/base/browser/mouseEvent'; </ins> import {TerminalInstance} from 'vs/workbench/parts/terminal/electron-browser/terminalInstance'; <ins> import {TPromise} from 'vs/base/common/winjs.base'; </ins> export class TerminalPanel extends Panel {

private terminalInstances: TerminalInstance[] = []; private actions: IAction[]; <ins> private contextMenuActions: IAction[]; </ins> private parentDomElement: HTMLElement; private terminalContainer: HTMLElement; private currentBaseThemeId: string;

constructor( @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService private configurationService: IConfigurationService, <ins> @IContextMenuService private contextMenuService: IContextMenuService, </ins> @IInstantiationService private instantiationService: IInstantiationService, <ins> @IKeybindingService private keybindingService: IKeybindingService, </ins> @IWorkspaceContextService private contextService: IWorkspaceContextService, @ITerminalService private terminalService: ITerminalService, @IThemeService private themeService: IThemeService,

this.instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL), this.instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL) ]; this.actions.forEach(a => { this.toDispose.push(a); }); } return this.actions; } <ins> private getContextMenuActions(): IAction[] { if (!this.contextMenuActions) { this.contextMenuActions = [ this.instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, nls.localize('createNewTerminal', "New terminal")), new Separator(), this.instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, nls.localize('copy', "Copy")), this.instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, nls.localize('paste', "Paste")) ]; this.contextMenuActions.forEach(a => { this.toDispose.push(a); }); } return this.contextMenuActions; } </ins> public getActionItem(action: Action): IActionItem { if (action.id === SwitchTerminalInstanceAction.ID) { return this.instantiationService.createInstance(SwitchTerminalInstanceActionItem, action);

this.parentDomElement.appendChild(this.fontStyleElement); this.parentDomElement.appendChild(this.terminalContainer); <ins> this.attachEventListeners(); </ins> this.configurationHelper = new TerminalConfigHelper(platform.platform, this.configurationService, parent); return this.terminalService.createNew(); } <ins> private attachEventListeners(): void { this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mousedown', (event: MouseEvent) => { if (this.terminalInstances.length === 0) { return; } if (event.which === 2 && platform.isLinux) { // Drop selection and focus terminal on Linux to enable middle button paste when click // occurs on the selection itself. this.terminalInstances[this.terminalService.getActiveTerminalIndex()].focus(true); } else if (event.which === 3) { // Trigger the context menu on right click let anchor: HTMLElement | { x: number, y: number } = this.parentDomElement; if (event instanceof MouseEvent) { const standardEvent = new StandardMouseEvent(event); anchor = { x: standardEvent.posx, y: standardEvent.posy }; } this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActions: () => TPromise.as(this.getContextMenuActions()), getActionsContext: () => this.parentDomElement, getKeyBinding: (action) => { const opts = this.keybindingService.lookupKeybindings(action.id); if (opts.length > 0) { return opts[0]; // only take the first one } return null; } }); } event.stopImmediatePropagation(); })); this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mouseup', (event) => { if (this.terminalInstances.length === 0) { return; } if (event.which !== 3) { this.terminalInstances[this.terminalService.getActiveTerminalIndex()].focus(); } })); this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'keyup', (event: KeyboardEvent) => { if (event.keyCode === 27) { // Keep terminal open on escape event.stopPropagation(); } })); } </ins> public createNewTerminalInstance(terminalProcess: ITerminalProcess, terminalFocusContextKey: IKeybindingContextKey<boolean>): TPromise<void> { return this.createTerminal(terminalProcess, terminalFocusContextKey).then(() => { this.updateConfig();

private createTerminal(terminalProcess: ITerminalProcess, terminalFocusContextKey: IKeybindingContextKey<boolean>): TPromise<TerminalInstance> { return new TPromise<TerminalInstance>(resolve => { <del> var terminalInstance = new TerminalInstance(terminalProcess, this.terminalContainer, this.contextService, this.terminalService, this.messageService, terminalFocusContextKey, this.onTerminalInstanceExit.bind(this)); </del> <ins> var terminalInstance = new TerminalInstance(terminalProcess, this.terminalContainer, this.contextMenuService, this.contextService, this.instantiationService, this.keybindingService, this.terminalService, this.messageService, terminalFocusContextKey, this.onTerminalInstanceExit.bind(this)); </ins> this.terminalInstances.push(terminalInstance); this.setActiveTerminal(this.terminalInstances.length - 1); this.toDispose.push(this.themeService.onDidThemeChange(this.updateTheme.bind(this)));