main.go 18.7 KB
Newer Older
liming6's avatar
liming6 committed
1
2
3
package tui

import (
4
	"get-container/cmd/hytop/backend"
liming6's avatar
liming6 committed
5
	"get-container/gpu"
liming6's avatar
liming6 committed
6
	"get-container/utils"
liming6's avatar
liming6 committed
7
	"slices"
liming6's avatar
liming6 committed
8
	"syscall"
liming6's avatar
liming6 committed
9
10
	"time"

liming6's avatar
liming6 committed
11
12
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
liming6's avatar
liming6 committed
13
14
15
16
)

const (
	DCUTopVersion = "1.0.0"
liming6's avatar
liming6 committed
17
18
)

liming6's avatar
liming6 committed
19
20
21
22
23
24
25
var (
	HeightLightStyle    = lipgloss.NewStyle().Background(lipgloss.Color("#00fffb7a")) // 高亮风格
	SelectedStyle       = lipgloss.NewStyle().Foreground(lipgloss.Color("#ffb81fe3")) // 被选中的风格
	HeightSelectedStyle = lipgloss.NewStyle().Background(lipgloss.Color("#ffb81fe3")) // 既被选中,又高亮的风格

	LowLeightStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#442d2d"))
	NormalStyle    = lipgloss.NewStyle().Foreground(lipgloss.Color("#e6e6e6eb"))
liming6's avatar
liming6 committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

	myBorder = lipgloss.Border{
		Top:          "═",
		TopLeft:      "╒",
		TopRight:     "╕",
		Bottom:       "═",
		BottomLeft:   "╘",
		BottomRight:  "╛",
		Left:         "│",
		Right:        "│",
		MiddleLeft:   "├",
		MiddleRight:  "┤",
		Middle:       "┼",
		MiddleTop:    "┬",
		MiddleBottom: "┴",
	}
liming6's avatar
liming6 committed
42
43
)

liming6's avatar
liming6 committed
44
45
// ModelMsg 模型信息,在父组件和各个子组件间共享信息
type ModelMsg struct {
liming6's avatar
liming6 committed
46
47
	t          time.Time                        // 当前时间
	Version    *gpu.HYVersionInfo               // gpu版本相关信息
48
49
	DCUPidInfo map[int][]backend.DCUProcessInfo // 使用dcu的进程信息
	systemInfo *utils.SysInfo                   // 系统信息
liming6's avatar
liming6 committed
50
51
	MyVersion  string                           // 本软件的版本
	DCUInfo    *backend.DCUInfoMap              // dcu相关信息
liming6's avatar
liming6 committed
52
53
54
}

type TickMsg time.Time
55
type ProcessAction int
liming6's avatar
liming6 committed
56
type ViewMode int
57
58
59
60
61
62

const (
	PAKill ProcessAction = 9
	PAInt  ProcessAction = 2
	PATerm ProcessAction = 15
	PANone ProcessAction = 0
liming6's avatar
liming6 committed
63
64
65
66
67

	VMMain       ViewMode = 0
	VMTree       ViewMode = 1
	VMOneProcess ViewMode = 2
	VMProcessEnv ViewMode = 3
liming6's avatar
liming6 committed
68
	VMHelp       ViewMode = 4
69
)
liming6's avatar
liming6 committed
70

liming6's avatar
liming6 committed
71
72
73
74
75
76
77
78
79
80
81
82
83
func (pa ProcessAction) String() string {
	switch pa {
	case PAKill:
		return "SIGKILL"
	case PAInt:
		return "SIGINT"
	case PATerm:
		return "SIGTERM"
	default:
		return "Cancel"
	}
}

liming6's avatar
liming6 committed
84
85
86
87
88
89
90
91
92
93
94
95
96
func (pa ProcessAction) Signal() syscall.Signal {
	switch pa {
	case PAKill:
		return syscall.SIGKILL
	case PAInt:
		return syscall.SIGINT
	case PATerm:
		return syscall.SIGTERM
	default:
		return syscall.SIGTERM
	}
}

liming6's avatar
liming6 committed
97
98
// ActionMsg 动作消息
type ActionMsg struct {
liming6's avatar
liming6 committed
99
	VM             ViewMode       // 所在视图,默认为0,即主视图
100
101
102
	SelectPids     map[int32]bool // 选择的pid进程
	Action         *ProcessAction // 对选择的pid的动作
	PidView        *int32         // 进程视图指标的pid号,为null表示没有进入进程指标视图
liming6's avatar
liming6 committed
103
	PointPid       *int32         // 指针指向的pid,为null表示没有选择进程,在主视图和进程树视图起作用
104
	PidEnvView     *int32         // 进程环境变量视图的pid号,为null表示不进入进程环境变量视图
liming6's avatar
liming6 committed
105
	PidTreePids    []int32        // pid tree视图下,进程id的顺序列表
106
	TargetDCUIndex *int           // PointPid使用的dcu index
liming6's avatar
liming6 committed
107
108
}

liming6's avatar
liming6 committed
109
110
// ModelMain tui主模型类
type ModelMain struct {
liming6's avatar
liming6 committed
111
112
113
	width, height int // 终端尺寸
	Header        *ModelHeader
	DCUInfo       *ModelDCUInfo
liming6's avatar
liming6 committed
114
	SysLoad       *ModelSysLoad
115
	ProcessInfo   *ModelProcessInfo
liming6's avatar
liming6 committed
116
	modelMsg      *ModelMsg
liming6's avatar
liming6 committed
117
	actionMsg     *ActionMsg
liming6's avatar
liming6 committed
118
119
	dialog        *Dialog      // 对话框
	pstree        *ModelPsTree // 进程树视图
liming6's avatar
liming6 committed
120
	processDetail *ModelProcessDetail
liming6's avatar
liming6 committed
121
122
}

liming6's avatar
liming6 committed
123
func NewModelMain(width, height int) ModelMain {
liming6's avatar
liming6 committed
124
	result := ModelMain{}
liming6's avatar
liming6 committed
125
126
	result.width = width
	result.height = height
127
128
	result.Header = NewModelHeader()
	result.DCUInfo = NewModelDCUInfo(width, height)
liming6's avatar
liming6 committed
129
	result.SysLoad = NewModelSysLoad(width)
130
	result.ProcessInfo = NewModelProcessInfo(width)
liming6's avatar
liming6 committed
131
132
	result.dialog = NewDialog(nil)
	result.pstree = NewModelPsTree(width, height)
liming6's avatar
liming6 committed
133
	result.actionMsg = &ActionMsg{}
liming6's avatar
liming6 committed
134
	return result
liming6's avatar
liming6 committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
}

func tickCmd() tea.Cmd {
	return tea.Every(time.Second, func(t time.Time) tea.Msg {
		return TickMsg(t)
	})
}

func sendMsgCmd(modelMsg *ModelMsg) tea.Cmd {
	return func() tea.Msg {
		return *modelMsg
	}
}

// Init 初始化信息
func (m *ModelMain) Init() tea.Cmd {
liming6's avatar
liming6 committed
151
152
	modelMsg := ModelMsg{}
	if err := initModelInfo(&modelMsg); err != nil {
liming6's avatar
liming6 committed
153
154
		return tea.Quit
	}
liming6's avatar
liming6 committed
155
156
157
158
	cmds := make([]tea.Cmd, 0)
	if c := m.Header.Init(); c != nil {
		cmds = append(cmds, c)
	}
liming6's avatar
liming6 committed
159
160
161
	if c := m.DCUInfo.Init(); c != nil {
		cmds = append(cmds, c)
	}
liming6's avatar
liming6 committed
162
163
164
	if c := m.SysLoad.Init(); c != nil {
		cmds = append(cmds, c)
	}
165
166
167
	if c := m.ProcessInfo.Init(); c != nil {
		cmds = append(cmds, c)
	}
liming6's avatar
liming6 committed
168
169
170
171
172
173
	if c := m.dialog.Init(); c != nil {
		cmds = append(cmds, c)
	}
	if c := m.pstree.Init(); c != nil {
		cmds = append(cmds, c)
	}
liming6's avatar
liming6 committed
174
	m.modelMsg = &modelMsg
liming6's avatar
liming6 committed
175
	// 将调用初始化方法
liming6's avatar
liming6 committed
176
	cmds = append(cmds, tickCmd(), sendMsgCmd(&modelMsg))
liming6's avatar
liming6 committed
177
178
179
180
181
182
183
	return tea.Batch(cmds...)
}

func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := inputMsg.(type) {
	case tea.KeyMsg: // 键盘事件
		switch msg.String() {
liming6's avatar
liming6 committed
184
		case "q":
liming6's avatar
liming6 committed
185
			return m, tea.Quit
liming6's avatar
liming6 committed
186
187
188
		case "ctrl+c":
			cmd := m.handleCtrlC()
			return m, cmd
liming6's avatar
liming6 committed
189
190
191
192
193
194
195
		case "up":
			cmd := m.handleKeyUp()
			return m, cmd
		case "down":
			cmd := m.handleKeyDown()
			return m, cmd
		case "enter":
liming6's avatar
liming6 committed
196
197
			cmd := m.handleKeyEnter()
			return m, cmd
liming6's avatar
liming6 committed
198
199
		case "h":
			return m, tea.Quit
liming6's avatar
liming6 committed
200
201
202
		case "left", "right":
			cmd := m.handleKeyLR(msg.String())
			return m, cmd
liming6's avatar
liming6 committed
203
		case "k":
204
205
			cmd := m.handleKeyK()
			return m, cmd
liming6's avatar
liming6 committed
206
207
208
209
210
211
		case "esc":
			cmd := m.handleKeyEsc()
			return m, cmd
		case " ":
			cmd := m.handleKeySpace()
			return m, cmd
liming6's avatar
liming6 committed
212
213
214
		case "t":
			cmd := m.handleKeyT()
			return m, cmd
liming6's avatar
liming6 committed
215
216
		}
	case TickMsg: // 定时事件
liming6's avatar
liming6 committed
217
		updateModelInfo(m.modelMsg, time.Time(msg))
liming6's avatar
liming6 committed
218
219
220
		header, _ := m.Header.Update(m.modelMsg)
		dcuInfo, _ := m.DCUInfo.Update(m.modelMsg)
		sysLoad, _ := m.SysLoad.Update(m.modelMsg)
221
		pidinfo, _ := m.ProcessInfo.Update(m.modelMsg)
liming6's avatar
liming6 committed
222
		pstree, _ := m.pstree.Update(m.modelMsg)
liming6's avatar
liming6 committed
223
		m.Header = header.(*ModelHeader)
liming6's avatar
liming6 committed
224
		m.DCUInfo = dcuInfo.(*ModelDCUInfo)
liming6's avatar
liming6 committed
225
		m.SysLoad = sysLoad.(*ModelSysLoad)
226
		m.ProcessInfo = pidinfo.(*ModelProcessInfo)
liming6's avatar
liming6 committed
227
		m.pstree = pstree.(*ModelPsTree)
liming6's avatar
liming6 committed
228
229
230
231
		if m.processDetail != nil {
			detail, _ := m.processDetail.Update(m.modelMsg)
			m.processDetail = detail.(*ModelProcessDetail)
		}
liming6's avatar
liming6 committed
232
		return m, tickCmd()
liming6's avatar
liming6 committed
233
234
235
	case ModelMsg: // 初始化
		header, _ := m.Header.Update(m.modelMsg)
		dcuInfo, _ := m.DCUInfo.Update(m.modelMsg)
liming6's avatar
liming6 committed
236
		sysLoad, _ := m.SysLoad.Update(m.modelMsg)
237
		pidinfo, _ := m.ProcessInfo.Update(m.modelMsg)
liming6's avatar
liming6 committed
238
239
		m.Header = header.(*ModelHeader)
		m.DCUInfo = dcuInfo.(*ModelDCUInfo)
liming6's avatar
liming6 committed
240
		m.SysLoad = sysLoad.(*ModelSysLoad)
241
		m.ProcessInfo = pidinfo.(*ModelProcessInfo)
liming6's avatar
liming6 committed
242
		return m, nil
243
	case tea.WindowSizeMsg: // 窗口尺寸变化
liming6's avatar
liming6 committed
244
		m.width, m.height = msg.Width, msg.Height
245
246
247
248
249
250
251
252
		header, _ := m.Header.Update(msg)
		dcuInfo, _ := m.DCUInfo.Update(msg)
		sysLoad, _ := m.SysLoad.Update(msg)
		pidinfo, _ := m.ProcessInfo.Update(msg)
		m.Header = header.(*ModelHeader)
		m.DCUInfo = dcuInfo.(*ModelDCUInfo)
		m.SysLoad = sysLoad.(*ModelSysLoad)
		m.ProcessInfo = pidinfo.(*ModelProcessInfo)
liming6's avatar
liming6 committed
253
		return m, nil
liming6's avatar
liming6 committed
254
255
256
257
	}
	return m, nil
}

liming6's avatar
liming6 committed
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
func (m *ModelMain) handleCtrlC() tea.Cmd {
	if m.actionMsg.Action != nil || m.actionMsg.VM != VMTree {
		return nil
	}
	if len(m.actionMsg.SelectPids) != 0 || m.actionMsg.PointPid != nil {
		action := PAInt
		m.actionMsg.Action = &action
		pstree, _ := m.pstree.Update(m.actionMsg)
		dialog, _ := m.dialog.Update(m.actionMsg)
		m.dialog = dialog.(*Dialog)
		m.pstree = pstree.(*ModelPsTree)
	}
	return nil
}

liming6's avatar
liming6 committed
273
func (m *ModelMain) handleKeyEnter() tea.Cmd {
liming6's avatar
liming6 committed
274
275
276
277
278
279
	if m.actionMsg.VM == VMMain && m.actionMsg.Action == nil {
		pid := *m.actionMsg.PointPid
		m.actionMsg.PidView = &pid
		m.processDetail = NewModelProcessDetail(m.width, m.height, pid)
		m.processDetail.Update(m.modelMsg)
		m.actionMsg.VM = VMOneProcess
liming6's avatar
liming6 committed
280
281
		return nil
	}
liming6's avatar
liming6 committed
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
	if m.actionMsg.Action != nil {
		target := make([]int32, 0, 8)
		if len(m.actionMsg.SelectPids) > 0 {
			for k := range m.actionMsg.SelectPids {
				target = append(target, k)
			}
		} else if m.actionMsg.PointPid != nil {
			target = append(target, *m.actionMsg.PointPid)
		}
		switch *m.actionMsg.Action {
		case PANone:
			m.actionMsg.Action = nil
			switch m.actionMsg.VM {
			case VMMain:
				header, _ := m.Header.Update(m.actionMsg)
				dcuInfo, _ := m.DCUInfo.Update(m.actionMsg)
				sysLoad, _ := m.SysLoad.Update(m.actionMsg)
				pidinfo, _ := m.ProcessInfo.Update(m.actionMsg)
				m.Header = header.(*ModelHeader)
				m.DCUInfo = dcuInfo.(*ModelDCUInfo)
				m.SysLoad = sysLoad.(*ModelSysLoad)
				m.ProcessInfo = pidinfo.(*ModelProcessInfo)
			case VMTree:
				pstree, _ := m.pstree.Update(m.actionMsg)
				m.pstree = pstree.(*ModelPsTree)
			}
		case PAKill:
			go utils.SendSignal(target, PAKill.Signal())
		case PATerm:
			go utils.SendSignal(target, PATerm.Signal())
		case PAInt:
			go utils.SendSignal(target, PAInt.Signal())
		}
	}
liming6's avatar
liming6 committed
316
317
318
	return nil
}

liming6's avatar
liming6 committed
319
320
321
322
323
324
func (m *ModelMain) handleKeyT() tea.Cmd {
	// 在主视图下,没有选择任何进程时,可以进入进程树视图,且
	if m.actionMsg.VM == VMMain && len(m.actionMsg.SelectPids) == 0 {
		m.actionMsg.VM = VMTree
		pstree, _ := m.pstree.Update(m.actionMsg)
		m.pstree = pstree.(*ModelPsTree)
325
		return nil
liming6's avatar
liming6 committed
326
327
328
329
330
331
332
333
334
335
336
337
	} else if m.actionMsg.VM == VMTree {
		if len(m.actionMsg.SelectPids) != 0 || m.actionMsg.PointPid != nil {
			action := PATerm
			m.actionMsg.Action = &action
			pstree, _ := m.pstree.Update(m.actionMsg)
			dialog, _ := m.dialog.Update(m.actionMsg)
			m.dialog = dialog.(*Dialog)
			m.pstree = pstree.(*ModelPsTree)
			return nil
		} else {
			return nil
		}
338
	}
liming6's avatar
liming6 committed
339
340
341
342
343
344
	return nil
}

// handleKeyLR 处理左右键消息,左右键仅在对话框中有效
func (m *ModelMain) handleKeyLR(s string) tea.Cmd {
	if m.actionMsg == nil || m.actionMsg.Action == nil {
345
346
		return nil
	}
liming6's avatar
liming6 committed
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
	switch s {
	case "right":
		switch *m.actionMsg.Action {
		case PAKill:
			*m.actionMsg.Action = PATerm
		case PATerm:
			*m.actionMsg.Action = PAInt
		case PAInt:
			*m.actionMsg.Action = PANone
		default:
			return nil
		}
	case "left":
		switch *m.actionMsg.Action {
		case PATerm:
			*m.actionMsg.Action = PAKill
		case PAInt:
			*m.actionMsg.Action = PATerm
		case PANone:
			*m.actionMsg.Action = PAInt
		default:
			return nil
		}
	default:
		return nil
372
	}
liming6's avatar
liming6 committed
373
374
	dialog, _ := m.dialog.Update(m.actionMsg)
	m.dialog = dialog.(*Dialog)
375
376
377
	return nil
}

liming6's avatar
liming6 committed
378
379
func (m *ModelMain) handleKeyK() tea.Cmd {
	if m.actionMsg.PointPid == nil && m.actionMsg.SelectPids == nil {
380
381
382
		return nil
	}
	if m.actionMsg.Action != nil {
liming6's avatar
liming6 committed
383
384
385
386
		return nil
	}
	switch m.actionMsg.VM {
	case VMMain:
liming6's avatar
liming6 committed
387
388
389
390
		var pa ProcessAction = PANone
		if m.actionMsg.Action == nil {
			m.actionMsg.Action = &pa
		}
391
392
393
394
395
396
397
398
		header, _ := m.Header.Update(m.actionMsg)
		dcuInfo, _ := m.DCUInfo.Update(m.actionMsg)
		sysLoad, _ := m.SysLoad.Update(m.actionMsg)
		pidinfo, _ := m.ProcessInfo.Update(m.actionMsg)
		m.Header = header.(*ModelHeader)
		m.DCUInfo = dcuInfo.(*ModelDCUInfo)
		m.SysLoad = sysLoad.(*ModelSysLoad)
		m.ProcessInfo = pidinfo.(*ModelProcessInfo)
liming6's avatar
liming6 committed
399
400
401
402
		dialog, _ := m.dialog.Update(m.actionMsg)
		m.dialog = dialog.(*Dialog)
		return nil
	case VMTree:
liming6's avatar
liming6 committed
403
404
405
406
		var pa ProcessAction = PAKill
		if m.actionMsg.Action == nil {
			m.actionMsg.Action = &pa
		}
liming6's avatar
liming6 committed
407
408
409
410
411
412
		pstree, _ := m.pstree.Update(m.actionMsg)
		dialog, _ := m.dialog.Update(m.actionMsg)
		m.dialog = dialog.(*Dialog)
		m.pstree = pstree.(*ModelPsTree)
		return nil
	default:
413
414
		return nil
	}
liming6's avatar
liming6 committed
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
}

// handleKeyEsc 处理Esc键,esc键在:
// VMMain视图下,取消所有选择的进程、退出单个进程模式或对话框返回
// VMTree视图下,取消所有选择的进程、退出单个进程模式或对话框返回,返回到VMMain视图
func (m *ModelMain) handleKeyEsc() tea.Cmd {
	switch m.actionMsg.VM {
	case VMMain:
		if m.actionMsg.Action != nil {
			m.actionMsg.Action = nil
		} else if m.actionMsg.SelectPids != nil {
			m.actionMsg.SelectPids = nil
		} else if m.actionMsg.PointPid != nil || m.actionMsg.TargetDCUIndex != nil {
			m.actionMsg.PointPid = nil
			m.actionMsg.TargetDCUIndex = nil
		}
431
432
433
434
435
436
437
438
		header, _ := m.Header.Update(m.actionMsg)
		dcuInfo, _ := m.DCUInfo.Update(m.actionMsg)
		sysLoad, _ := m.SysLoad.Update(m.actionMsg)
		pidinfo, _ := m.ProcessInfo.Update(m.actionMsg)
		m.Header = header.(*ModelHeader)
		m.DCUInfo = dcuInfo.(*ModelDCUInfo)
		m.SysLoad = sysLoad.(*ModelSysLoad)
		m.ProcessInfo = pidinfo.(*ModelProcessInfo)
liming6's avatar
liming6 committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
		return nil
	case VMTree:
		if m.actionMsg.Action != nil {
			m.actionMsg.Action = nil
		} else if m.actionMsg.SelectPids != nil {
			m.actionMsg.SelectPids = nil
		} else if m.actionMsg.PointPid != nil {
			m.actionMsg.PointPid = nil
		} else {
			m.actionMsg.VM = VMMain
		}
		pstree, _ := m.pstree.Update(m.actionMsg)
		m.pstree = pstree.(*ModelPsTree)
		return nil
liming6's avatar
liming6 committed
453
454
455
456
	case VMOneProcess:
		m.actionMsg.VM = VMMain
		m.processDetail = nil
		return nil
liming6's avatar
liming6 committed
457
458
	default:
		return nil
459
	}
liming6's avatar
liming6 committed
460
461
}

liming6's avatar
liming6 committed
462
// handleKeySpace 处理空格键动作,空格键仅用于选择或取消进程
liming6's avatar
liming6 committed
463
func (m *ModelMain) handleKeySpace() tea.Cmd {
liming6's avatar
liming6 committed
464
465
466
467
468
469
470
	if len(m.modelMsg.DCUPidInfo) == 0 {
		return nil
	}
	if m.actionMsg.PointPid == nil || m.actionMsg.Action != nil {
		return nil
	}
	if m.actionMsg.VM != VMMain && m.actionMsg.VM != VMTree {
liming6's avatar
liming6 committed
471
472
473
474
475
476
477
478
479
480
481
		return nil
	}
	if m.actionMsg.SelectPids == nil {
		m.actionMsg.SelectPids = make(map[int32]bool)
	}
	_, have := m.actionMsg.SelectPids[*m.actionMsg.PointPid]
	if have {
		delete(m.actionMsg.SelectPids, *m.actionMsg.PointPid)
	} else {
		m.actionMsg.SelectPids[*m.actionMsg.PointPid] = true
	}
liming6's avatar
liming6 committed
482
483
484
485
486
487
488
489
490
491
492
493
	switch m.actionMsg.VM {
	case VMMain:
		m1, cmd1 := m.ProcessInfo.Update(m.actionMsg)
		m.ProcessInfo = m1.(*ModelProcessInfo)
		return cmd1
	case VMTree:
		m1, cmd1 := m.pstree.Update(m.actionMsg)
		m.pstree = m1.(*ModelPsTree)
		return cmd1
	default:
		return nil
	}
liming6's avatar
liming6 committed
494
495
}

liming6's avatar
liming6 committed
496
func (m *ModelMain) View() string {
liming6's avatar
liming6 committed
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
	switch m.actionMsg.VM {
	case VMMain:
		if m.actionMsg.Action != nil {
			up := m.dialog.View()
			down := m.Header.View() + m.DCUInfo.View() + m.SysLoad.View() + m.ProcessInfo.View() + "\n"
			return StackPosition(up, down, lipgloss.Center, lipgloss.Center)
		}
		return m.Header.View() + m.DCUInfo.View() + m.SysLoad.View() + m.ProcessInfo.View() + "\n"
	case VMTree:
		if m.actionMsg.Action != nil {
			up := m.dialog.View()
			down := m.pstree.View()
			return StackPosition(up, down, lipgloss.Center, lipgloss.Center)
		}
		return m.pstree.View()
liming6's avatar
liming6 committed
512
513
	case VMOneProcess:
		return m.processDetail.View()
liming6's avatar
liming6 committed
514
515
516
	default:
		return ""
	}
liming6's avatar
liming6 committed
517
518
}

liming6's avatar
liming6 committed
519
520
func initModelInfo(model *ModelMsg) error {
	model.t = time.Now()
liming6's avatar
liming6 committed
521
522
	model.MyVersion = DCUTopVersion
	if ver, err := gpu.GetHYVersionInfo(); err != nil {
liming6's avatar
liming6 committed
523
		return err
liming6's avatar
liming6 committed
524
	} else {
liming6's avatar
liming6 committed
525
		model.Version = ver
liming6's avatar
liming6 committed
526
	}
527
528
529
530
531
532
533
534
535
536
537
538
539
540
	model.DCUInfo = backend.DCUSInfoMap
	model.DCUPidInfo = backend.DCUSInfoMap.GetDCUProcessInfo2()
	if sinfo, err := utils.GetSysInfo(); err != nil {
		return err
	} else {
		model.systemInfo = sinfo
	}
	if err := model.DCUInfo.UpdateQuickInfo(); err != nil {
		return err
	}
	if err := model.DCUInfo.UpdateSlowInfo(); err != nil {
		return err
	}
	return nil
liming6's avatar
liming6 committed
541
542
}

liming6's avatar
liming6 committed
543
// updateModelInfo 更新模型信息
544
func updateModelInfo(modelMsg *ModelMsg, t time.Time) error {
liming6's avatar
liming6 committed
545
	modelMsg.t = t
546
547
548
549
550
551
552
553
554
555
	if sinfo, err := utils.GetSysInfo(); err != nil {
		return err
	} else {
		modelMsg.systemInfo = sinfo
	}
	modelMsg.DCUPidInfo = modelMsg.DCUInfo.GetDCUProcessInfo2()
	if err := modelMsg.DCUInfo.UpdateQuickInfo(); err != nil {
		return err
	}
	return nil
liming6's avatar
liming6 committed
556
}
liming6's avatar
liming6 committed
557
558

func (m *ModelMain) handleKeyUp() tea.Cmd {
liming6's avatar
liming6 committed
559
	if len(m.modelMsg.DCUPidInfo) == 0 {
liming6's avatar
liming6 committed
560
561
		return nil
	}
562
563
564
	if m.actionMsg.Action != nil {
		return nil
	}
liming6's avatar
liming6 committed
565
566
	switch m.actionMsg.VM {
	case VMMain:
liming6's avatar
liming6 committed
567
568
569
570
571
572
573
		processes := make([]backend.DCUProcessInfo, 0, 16)
		keys := make([]int, 0, 16)
		for k := range m.modelMsg.DCUPidInfo {
			keys = append(keys, k)
		}
		slices.Sort(keys)
		for _, index := range keys {
liming6's avatar
liming6 committed
574
575
576
			p, have := m.modelMsg.DCUPidInfo[index]
			if have && len(p) > 0 {
				processes = append(processes, p...)
liming6's avatar
liming6 committed
577
578
			}
		}
liming6's avatar
liming6 committed
579
580
581
582
583
584
585
		processNum := len(processes)
		if processNum == 0 {
			return nil
		}
		index := 0
		if m.actionMsg.PointPid == nil {
			// 获取列表中的最后一个进程的pid
liming6's avatar
liming6 committed
586
587
			index = processNum - 1
		} else {
liming6's avatar
liming6 committed
588
589
590
591
592
593
594
595
596
597
598
599
			var targetIndex = -1
			for l := processNum - 1; l >= 0; l-- {
				if processes[l].Info.Pid == *m.actionMsg.PointPid {
					targetIndex = l - 1
					break
				}
			}
			if targetIndex == -1 {
				index = processNum - 1
			} else {
				index = targetIndex
			}
liming6's avatar
liming6 committed
600
		}
liming6's avatar
liming6 committed
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
		pid := processes[index].Info.Pid
		m.actionMsg.PointPid = &pid
		idx := processes[index].DCU
		m.actionMsg.TargetDCUIndex = &idx
		m1, cmd1 := m.ProcessInfo.Update(m.actionMsg)
		m2, cmd2 := m.DCUInfo.Update(m.actionMsg)
		m3, cmd3 := m.SysLoad.Update(m.actionMsg)
		m.ProcessInfo = m1.(*ModelProcessInfo)
		m.DCUInfo = m2.(*ModelDCUInfo)
		m.SysLoad = m3.(*ModelSysLoad)
		return tea.Batch(cmd1, cmd2, cmd3)
	case VMTree:
		if len(m.actionMsg.PidTreePids) == 0 {
			return nil
		}
		if m.actionMsg.PointPid == nil {
			pid := m.actionMsg.PidTreePids[len(m.actionMsg.PidTreePids)-1]
			m.actionMsg.PointPid = &pid
		} else {
			idx := slices.Index(m.actionMsg.PidTreePids, *m.actionMsg.PointPid)
			if idx == -1 || idx == 0 {
				*m.actionMsg.PointPid = m.actionMsg.PidTreePids[len(m.actionMsg.PidTreePids)-1]
			} else {
				*m.actionMsg.PointPid = m.actionMsg.PidTreePids[idx-1]
			}
		}
		pstree, _ := m.pstree.Update(m.actionMsg)
		m.pstree = pstree.(*ModelPsTree)
		return nil
	default:
		return nil
liming6's avatar
liming6 committed
632
	}
633

liming6's avatar
liming6 committed
634
635
636
}

func (m *ModelMain) handleKeyDown() tea.Cmd {
liming6's avatar
liming6 committed
637
	if len(m.modelMsg.DCUPidInfo) == 0 {
liming6's avatar
liming6 committed
638
639
		return nil
	}
640
641
642
	if m.actionMsg.Action != nil {
		return nil
	}
liming6's avatar
liming6 committed
643
644
	switch m.actionMsg.VM {
	case VMMain:
liming6's avatar
liming6 committed
645
646
647
648
649
650
651
		processes := make([]backend.DCUProcessInfo, 0, 16)
		keys := make([]int, 0, 16)
		for key := range m.modelMsg.DCUPidInfo {
			keys = append(keys, key)
		}
		slices.Sort(keys)
		for _, index := range keys {
liming6's avatar
liming6 committed
652
653
654
			p, have := m.modelMsg.DCUPidInfo[index]
			if have && len(p) > 0 {
				processes = append(processes, p...)
liming6's avatar
liming6 committed
655
656
			}
		}
liming6's avatar
liming6 committed
657
658
659
660
661
662
		processNum := len(processes)
		if processNum == 0 {
			return nil
		}
		index := 0
		if m.actionMsg.PointPid == nil {
liming6's avatar
liming6 committed
663
664
			index = 0
		} else {
liming6's avatar
liming6 committed
665
666
667
668
669
670
671
672
673
674
675
676
677
678
			var targetIndex = -1
			for l := 0; l < processNum-1; l++ {
				if processes[l].Info.Pid == *m.actionMsg.PointPid {
					targetIndex = l + 1
					if targetIndex >= processNum {
						targetIndex = 0
					}
					break
				}
			}
			if targetIndex == -1 {
				index = 0
			} else {
				index = targetIndex
liming6's avatar
liming6 committed
679

liming6's avatar
liming6 committed
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
			}
		}
		pid := processes[index].Info.Pid
		m.actionMsg.PointPid = &pid
		idx := processes[index].DCU
		m.actionMsg.TargetDCUIndex = &idx
		m1, cmd1 := m.ProcessInfo.Update(m.actionMsg)
		m2, cmd2 := m.DCUInfo.Update(m.actionMsg)
		m3, cmd3 := m.SysLoad.Update(m.actionMsg)
		m.ProcessInfo = m1.(*ModelProcessInfo)
		m.DCUInfo = m2.(*ModelDCUInfo)
		m.SysLoad = m3.(*ModelSysLoad)
		return tea.Batch(cmd1, cmd2, cmd3)
	case VMTree:
		if len(m.actionMsg.PidTreePids) == 0 {
			return nil
liming6's avatar
liming6 committed
696
		}
liming6's avatar
liming6 committed
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
		if m.actionMsg.PointPid == nil {
			pid := m.actionMsg.PidTreePids[0]
			m.actionMsg.PointPid = &pid
		} else {
			idx := slices.Index(m.actionMsg.PidTreePids, *m.actionMsg.PointPid)
			if idx == -1 || idx == len(m.actionMsg.PidTreePids)-1 {
				*m.actionMsg.PointPid = m.actionMsg.PidTreePids[0]
			} else {
				*m.actionMsg.PointPid = m.actionMsg.PidTreePids[idx+1]
			}
		}
		pstree, _ := m.pstree.Update(m.actionMsg)
		m.pstree = pstree.(*ModelPsTree)
		return nil
	default:
		return nil
liming6's avatar
liming6 committed
713
	}
liming6's avatar
liming6 committed
714

liming6's avatar
liming6 committed
715
}