⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 devtv.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 3 页
字号:
/*  * Driver for Bt848 TV tuner.    *  */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h"#include "io.h"#include "hcwAMC.h"#define max(a, b)	(((a) > (b))? (a): (b))enum {	Qdir = 0,	Qsubdir,	Qsubbase,	Qvdata = Qsubbase,	Qadata,	Qctl,	Qregs,	Brooktree_vid = 0x109e,	Brooktree_848_did = 0x0350,	Brooktree_878_did = 0x036E,	Intel_vid = 0x8086,	Intel_82437_did = 0x122d,	K = 1024,	M = K * K,	Ntvs = 4,	Numring = 16,	ntsc_rawpixels = 910,	ntsc_sqpixels = 780,					// Including blanking & inactive	ntsc_hactive = 640,	ntsc_vactive = 480,	ntsc_clkx1delay = 135,				// Clock ticks.	ntsc_clkx1hactive = 754,	ntsc_vdelay = 26,					// # of scan lines.	ntsc_vscale = 0,	i2c_nostop = 1 << 5,	i2c_nos1b = 1 << 4,	i2c_timing = 7 << 4,	i2c_bt848w3b = 1 << 2,	i2c_bt848scl = 1 << 1,	i2c_bt848sda = 1 << 0,	i2c_scl = i2c_bt848scl,	i2c_sda = i2c_bt848sda,	i2c_miroproee = 0x80,				// MIRO PRO EEPROM	i2c_tea6300 = 0x80,	i2c_tda8425 = 0x82,	i2c_tda9840 = 0x84,	i2c_tda9850 = 0xb6,	i2c_haupee = 0xa0,					// Hauppage EEPROM	i2c_stbee = 0xae,					// STB EEPROM	i2c_msp3400 = 0x80,	i2c_timeout = 1000,	i2c_delay = 10,	Bt848_miropro = 0,	Bt848_miro,	Bt878_hauppauge,	// Bit fields.	iform_muxsel1 = 3 << 5,				// 004	iform_muxsel0 = 2 << 5,		iform_xtselmask = 3 << 3,	iform_xtauto = 3 << 3,	iform_formatmask = 7 << 0,	iform_ntsc = 1 << 0,	control_ldec = 1 << 5,				// 02C	contrast_100percent = 0xd8,			// 030	vscale_interlaced = 1 << 5,			// 04C	adelay_ntsc = 104,					// 060	bdelay_ntsc = 93,					// 064	adc_crush = 1 << 0,					// 068	colorfmt_rgb16 = (2 << 4) | (2 << 0),	// 0D4	colorfmt_YCbCr422 = (8 << 4) | (8 << 0),	colorfmt_YCbCr411 = (9 << 4) | (9 << 0),	colorctl_gamma = 1 << 4,			// 0D8	capctl_fullframe = 1 << 4,				// 0DC	capctl_captureodd = 1 << 1,	capctl_captureeven = 1 << 0,	vbipacksize = 0x190,				// 0E0	intstat_riscstatshift = 28,				// 100	intstat_i2crack = 1 << 25,	intstat_scerr = 1 << 19,	intstat_ocerr = 1 << 18,	intstat_pabort = 1 << 17,	intstat_riperr = 1 << 16,	intstat_pperr = 1 << 15,	intstat_fdsr = 1 << 14,	intstat_ftrgt = 1 << 13,	intstat_fbus = 1 << 12,	intstat_risci = 1 << 11,	intstat_i2cdone = 1 << 8,		intstat_vpress = 1 << 5,	intstat_hlock = 1 << 4,	intstat_vsync = 1 << 1,	intstat_fmtchg = 1 << 0,	intmask_etbf = 1 << 23,				// 104	gpiodmactl_apwrdn = 1 << 26,			// 10C	gpiodmactl_daes2 = 1 << 13,			gpiodmactl_daiomda = 1 << 6,	gpiodmactl_pltp23_16 = 2 << 6,	gpiodmactl_pltp23_0 = 0 << 6,		gpiodmactl_pltp1_16 = 2 << 4,	gpiodmactl_pltp1_0 = 0 << 4,	gpiodmactl_acapenable = 1 << 4,	gpiodmactl_pktp_32 = 3 << 2,			gpiodmactl_pktp_0 = 0 << 2,			gpiodmactl_riscenable = 1 << 1,	gpiodmactl_fifoenable = 1 << 0,		// RISC instructions and parameters.	fifo_vre = 0x4,	fifo_vro = 0xc,	fifo_fm1 = 0x6,	fifo_fm3 = 0xe,	riscirq = 1 << 24,	riscwrite = 1 << 28,	riscwrite123 = 9 << 28,	riscwrite1s23 = 11 << 28,		riscwrite_sol = 1 << 27,		riscwrite_eol = 1 << 26,	riscskip = 0x2 << 28,	riscjmp = 0x7 << 28,	riscsync = 0x8 << 28,		riscsync_resync = 1 << 15,		riscsync_vre = fifo_vre << 0,		riscsync_vro = fifo_vro << 0,		riscsync_fm1 = fifo_fm1 << 0,		riscsync_fm3 = fifo_fm3 << 0,	risclabelshift_set = 16,	risclabelshift_reset = 20,	AudioTuner = 0,	AudioRadio,	AudioExtern,	AudioIntern,	AudioOff,	AudioOn,		asel_tv = 0,	asel_radio,	asel_mic,	asel_smxc,	Hwbase_ad = 448000,	msp_dem = 0x10,	msp_bbp = 0x12,	// Altera definitions.	gpio_altera_data = 1 << 0,	gpio_altera_clock = 1 << 20,	gpio_altera_nconfig = 1 << 23,	Ial = 0x140001,	Idma = 0x100002,	Adsp = 0x7fd8,	Adsp_verifysystem = 1,	Adsp_querysupportplay,	Adsp_setstyle,	Adsp_setsrate,	Adsp_setchannels,	Adsp_setresolution,	Adsp_setcrcoptions,	Adsp_bufenqfor,	Adsp_logbuffer,	Adsp_startplay,	Adsp_stopplay,	Adsp_autostop,	Adsp_startrecord,	Adsp_stoprecord,	Adsp_getlastprocessed,	Adsp_pause,	Adsp_resume,	Adsp_setvolume,	Adsp_querysupportrecord,	Adsp_generalbufenq,	Adsp_setdownmixtype,	Adsp_setigain,	Adsp_setlineout,	Adsp_setlangmixtype,	Kfir_gc = 0,	Kfir_dsp_riscmc,	Kfir_dsp_risccram,	Kfir_dsp_unitmc,	Kfir_bsm_mc,	Kfir_mux_mc,	Kfir_devid_gc = 7,	Kfir_devid_dsp = 4,	Kfir_devid_bsm = 5,	Kfir_devid_mux = 8,	Kfir_200 = 200,	Kfir_dev_inst = Kfir_200,	Kfir_201 = 201,	Kfir_exec = Kfir_201,	Kfir_202 = 202,	Kfir_adr_eready = 254,	Kfir_d_eready_encoding = 0,	Kfir_d_eready_ready,	Kfir_d_eready_test,	Kfir_d_eready_stopdetect,	Kfir_d_eready_seqend,	VT_KFIR_OFF = 0,	VT_KFIR_ON,	VT_KFIR_LAYER_II = 1,	VT_KFIR_STEREO = 1,	Gpioinit = 0,	Gpiooutput,	Gpioinput,	Srate_5512 = 0,	Srate_11025 = 2,	Srate_16000 = 3,	Srate_22050 = 4,	Srate_32000 = 5,	Srate_44100 = 6,	Srate_48000 = 7,};typedef struct Variant Variant;struct Variant {	ushort		vid;	ushort		did;	char			*name;};typedef struct Bt848 Bt848;struct Bt848 {	ulong	devstat;		// 000	ulong	iform;		// 004	ulong	tdec;			// 008	ulong	ecrop;		// 00C	ulong	evdelaylo;		// 010	ulong	evactivelo;	// 014	ulong	ehdelaylo;		// 018	ulong	ehactivelo;	// 01C	ulong	ehscalehi;		// 020	ulong	ehscalelo;		// 024	ulong	bright;		// 028	ulong	econtrol;		// 02C	ulong	contrastlo;	// 030	ulong	satulo;		// 034	ulong	satvlo;		// 038	ulong	hue;			// 03C	ulong	escloop;		// 040	ulong	pad0;		// 044	ulong	oform;		// 048	ulong	evscalehi;		// 04C	ulong	evscalelo;		// 050	ulong	test;			// 054	ulong	pad1[2];		// 058-05C	ulong	adelay;		// 060	ulong	bdelay;		// 064	ulong	adc;			// 068	ulong	evtc;			// 06C	ulong	pad2[3];		// 070-078	ulong	sreset;		// 07C	ulong	tglb;			// 080	ulong	tgctrl;		// 084	ulong	pad3;		// 088	ulong	ocrop;		// 08C	ulong	ovdelaylo;		// 090	ulong	ovactivelo;	// 094	ulong	ohdelaylo;	// 098	ulong	ohactivelo;	// 09C	ulong	ohscalehi;		// 0A0	ulong	ohscalelo;		// 0A4	ulong	pad4;		// 0A8	ulong	ocontrol;		// 0AC	ulong	pad5[4];		// 0B0-0BC	ulong	oscloop;		// 0C0	ulong	pad6[2];		// 0C4-0C8	ulong	ovscalehi;		// 0CC	ulong	ovscalelo;		// 0D0	ulong	colorfmt;		// 0D4	ulong	colorctl;		// 0D8	ulong	capctl;		// 0DC	ulong	vbipacksize;	// 0E0	ulong	vbipackdel;	// 0E4	ulong	fcap;			// 0E8	ulong	ovtc;			// 0EC	ulong	pllflo;		// 0F0	ulong	pllfhi;		// 0F4	ulong	pllxci;		// 0F8	ulong	dvsif;		// 0FC	ulong	intstat;		// 100	ulong	intmask;		// 104	ulong	pad7;		// 108	ulong	gpiodmactl;	// 10C	ulong	i2c;			// 110	ulong	riscstrtadd;	// 114	ulong	gpioouten;	// 118	ulong	gpioreginp;	// 11C	ulong	risccount;		// 120	ulong	pad8[55];		// 124-1FC		ulong	gpiodata[64];	// 200-2FC};#define packetlen	i2ctypedef struct Tuner Tuner;struct Tuner {	char		*name;  	ushort	freq_vhfh;	// Start frequency	ushort	freq_uhf;  	uchar	VHF_L;	uchar	VHF_H;	uchar	UHF;	uchar	cfg; 	ushort	offs;};typedef struct Frame Frame;struct Frame {	ulong	*fstart;	ulong	*fjmp;	uchar	*fbase;};typedef struct Tv Tv;struct Tv {	Lock;	Rendez;	Bt848	*bt848;	Bt848	*bt878;			// Really only audio control registers	Variant	*variant;	Tuner	*tuner;	Pcidev	*pci;	uchar	i2ctuneraddr;	uchar	i2ccmd;			// I2C command	int		board;			// What board is this?	ulong	cfmt;			// Current color format.	int		channel;			// Current channel	Ref		fref;				// Copying images?	int		nframes;			// Number of frames to capture.	Frame	*frames;			// DMA program	int		lvframe;			// Last video frame DMAed	uchar	*amux;			// Audio multiplexer.	int		nablocks;			// Number of audio blocks allocated	int		absize;			// Audio block size	int		narblocks;		// Number of audio blocks received	ulong	*arisc;			// Audio risc bloc	uchar	*abuf;			// Audio data buffers	char		ainfo[128];	// WinTV/PVR stuff.	int		msp;		Lock		kfirlock;	ulong	i2cstate;			// Last i2c state.	int		gpiostate;			// Current GPIO state	ulong	alterareg;			// Last used altera register	ulong	alteraclock;		// Used to clock the altera	int		asrate;			// Audio sample rate	uchar	aleft, aright;		// Left and right audio volume	ulong	kfirclock;	Ref		aref;				// Copying audio?};enum {	TemicPAL = 0,	PhilipsPAL,	PhilipsNTSC,	PhilipsSECAM,	Notuner,	PhilipsPALI,	TemicNTSC,	TemicPALI,	Temic4036,	AlpsTSBH1,	AlpsTSBE1,	Freqmultiplier = 16,};static Tuner tuners[] = {        {"Temic PAL", Freqmultiplier * 140.25, Freqmultiplier * 463.25, 		0x02, 0x04, 0x01, 0x8e, 623 },	{"Philips PAL_I", Freqmultiplier * 140.25, Freqmultiplier * 463.25, 		0xa0, 0x90, 0x30, 0x8e, 623 },	{"Philips NTSC",  Freqmultiplier * 157.25, Freqmultiplier * 451.25, 		0xA0, 0x90, 0x30, 0x8e, 732 },	{"Philips SECAM", Freqmultiplier * 168.25, Freqmultiplier * 447.25, 		0xA7, 0x97, 0x37, 0x8e, 623 },	{"NoTuner", 0, 0, 		0x00, 0x00, 0x00, 0x00, 0 },	{"Philips PAL", Freqmultiplier * 168.25, Freqmultiplier * 447.25, 		0xA0, 0x90, 0x30, 0x8e, 623 },	{"Temic NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25, 		0x02, 0x04, 0x01, 0x8e, 732 },	{"TEMIC PAL_I", Freqmultiplier * 170.00, Freqmultiplier * 450.00, 		0x02, 0x04, 0x01, 0x8e, 623 },	{"Temic 4036 FY5 NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25, 		0xa0, 0x90, 0x30, 0x8e, 732 },	{"Alps TSBH1", Freqmultiplier * 137.25, Freqmultiplier * 385.25, 		0x01, 0x02, 0x08, 0x8e, 732 },	{"Alps TSBE1", Freqmultiplier * 137.25, Freqmultiplier * 385.25, 		0x01, 0x02, 0x08, 0x8e, 732 },};static int hp_tuners[] = {	Notuner,          	Notuner,            	Notuner,          	Notuner,          	Notuner,            	PhilipsNTSC,          	Notuner,           	Notuner,            	PhilipsPAL,           	PhilipsSECAM,         	PhilipsNTSC,        	PhilipsPALI,         	Notuner,                	Notuner,            	TemicPAL,             	TemicPALI,           	Notuner,                	PhilipsSECAM,         	PhilipsNTSC,         	PhilipsPALI,         	Notuner,             	PhilipsPAL,         	Notuner,               	PhilipsNTSC,};enum {	CMvstart,	CMastart,	CMastop,	CMvgastart,	CMvstop,	CMchannel,	CMcolormode,	CMvolume,	CMmute,};static Cmdtab tvctlmsg[] = {	CMvstart,			"vstart",			2,	CMastart,			"astart",			5,	CMastop,			"astop",			1,	CMvgastart,		"vgastart",		3,	CMvstop,			"vstop",			1,	CMchannel,		"channel",			3,	CMcolormode,		"colormode",		2,	CMvolume,		"volume",			3,	CMmute,			"mute",			1,};static Variant variant[] = {{	Brooktree_vid,	Brooktree_848_did,	"Brooktree 848 TV tuner",	},{	Brooktree_vid,	Brooktree_878_did,	"Brooktree 878 TV tuner",	},};static char *boards[] = {	"MIRO PRO",	"MIRO",	"Hauppauge Bt878",};static ushort Adspfsample[] = {	0x500, 0x700, 0x400, 0x600, 0x300, 0x200, 0x000, 0x100};static ushort Adspstereorates[] = {	64, 96, 112, 128, 160, 192, 224, 256, 320, 384};static uchar miroamux[] = { 2, 0, 0, 0, 10, 0 };static uchar hauppaugeamux[] = { 0, 1, 2, 3, 4, 0 };static char *nicamstate[] = {	"analog", "???", "digital", "bad digital receiption" };static Tv tvs[Ntvs];static int ntvs;static int i2cread(Tv *, uchar, uchar *);static int i2cwrite(Tv *, uchar, uchar, uchar, int);static void tvinterrupt(Ureg *, Tv *);static void vgastart(Tv *, ulong, int);static void vstart(Tv *, int, int, int, int);static void astart(Tv *, char *, uint, uint, uint);static void vstop(Tv *);static void astop(Tv *);static void colormode(Tv *, char *);static void frequency(Tv *, int, int);static int getbitspp(Tv *);static char *getcolormode(ulong);static int mspreset(Tv *);static void i2cscan(Tv *);static int kfirinitialize(Tv *);static void msptune(Tv *);static void mspvolume(Tv *, int, int, int);static void gpioenable(Tv *, ulong, ulong);static void gpiowrite(Tv *, ulong, ulong);static voidtvinit(void){	Pcidev *pci;	ulong intmask;	// Test for a triton memory controller.	intmask = 0;	if (pcimatch(nil, Intel_vid, Intel_82437_did))		intmask = intmask_etbf;	pci = nil;	while ((pci = pcimatch(pci, 0, 0)) != nil) {		int i, t;		Tv *tv;		Bt848 *bt848;		ushort hscale, hdelay;		uchar v;		for (i = 0; i != nelem(variant); i++)			if (pci->vid == variant[i].vid && pci->did == variant[i].did)				break;		if (i == nelem(variant))			continue;		if (ntvs >= Ntvs) {			print("#V: Too many TV cards found\n");			continue;		}		tv = &tvs[ntvs++];		tv->variant = &variant[i];		tv->pci = pci;		tv->bt848 = (Bt848 *)vmap(pci->mem[0].bar & ~0x0F, 4 * K);		if (tv->bt848 == nil)			panic("#V: Cannot allocate memory for Bt848");		bt848 = tv->bt848;		// i2c stuff.		if (pci->did >= 878)			tv->i2ccmd = 0x83;		else 			tv->i2ccmd = i2c_timing | i2c_bt848scl | i2c_bt848sda;		t = 0;		if (i2cread(tv, i2c_haupee, &v)) {			uchar ee[256];			Pcidev *pci878;			Bt848 *bt878;			tv->board = Bt878_hauppauge;			if (!i2cwrite(tv, i2c_haupee, 0, 0, 0))				panic("#V: Cannot write to Hauppauge EEPROM");			for (i = 0; i != sizeof ee; i++)				if (!i2cread(tv, i2c_haupee + 1, &ee[i]))					panic("#V: Cannot read from Hauppauge EEPROM");			if (ee[9] > sizeof hp_tuners / sizeof hp_tuners[0])				panic("#V: Tuner out of range (max %d, this %d)",					sizeof hp_tuners / sizeof hp_tuners[0], ee[9]);			t = hp_tuners[ee[9]];			// Initialize the audio channel.			if ((pci878 = pcimatch(nil, Brooktree_vid, 0x878)) == nil)				panic("#V: Unsupported Hauppage board");			tv->bt878 = bt878 = 				(Bt848 *)vmap(pci878->mem[0].bar & ~0x0F, 4 * K);			if (bt878 == nil)				panic("#V: Cannot allocate memory for the Bt878");			kfirinitialize(tv);			// i2cscan(tv);			mspreset(tv);			bt878->gpiodmactl = 0;			bt878->intstat = (ulong)-1;			intrenable(pci878->intl, (void (*)(Ureg *, void *))tvinterrupt, 					tv, pci878->tbdf, "tv");				tv->amux = hauppaugeamux;		}		else if (i2cread(tv, i2c_stbee, &v)) {			USED(t);			panic("#V: Cannot deal with STB cards\n");		}		else if (i2cread(tv, i2c_miroproee, &v)) {			tv->board = Bt848_miropro;			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;			tv->amux = miroamux;		}		else {			tv->board = Bt848_miro;			tv->amux = miroamux;			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;		}		if (t >= nelem(tuners))			t = 4;		tv->tuner = &tuners[t];		tv->i2ctuneraddr = 			i2cread(tv, 0xc1, &v)? 0xc0:			i2cread(tv, 0xc3, &v)? 0xc2:			i2cread(tv, 0xc5, &v)? 0xc4:			i2cread(tv, 0xc7, &v)? 0xc6: -1;		bt848->capctl = capctl_fullframe;		bt848->adelay = adelay_ntsc;		bt848->bdelay = bdelay_ntsc;		bt848->iform = iform_muxsel0|iform_xtauto|iform_ntsc;		bt848->vbipacksize = vbipacksize & 0xff;		bt848->vbipackdel = (vbipacksize >> 8) & 1;		// setpll(bt848);		tv->cfmt = bt848->colorfmt = colorfmt_rgb16;		hscale = (ntsc_rawpixels * 4096) / ntsc_sqpixels - 4096;		hdelay = (ntsc_clkx1delay * ntsc_hactive) / ntsc_clkx1hactive;		bt848->ovtc = bt848->evtc = 0;		bt848->ehscalehi = bt848->ohscalehi = (hscale >> 8) & 0xff;		bt848->ehscalelo = bt848->ohscalelo = hscale & 0xff;		bt848->evscalehi &= ~0x1f;		bt848->ovscalehi &= ~0x1f;		bt848->evscalehi |= vscale_interlaced | ((ntsc_vscale >> 8) & 0x1f);		bt848->ovscalehi |= vscale_interlaced | (ntsc_vscale >> 8) & 0x1f;		bt848->evscalelo = bt848->ovscalelo = ntsc_vscale & 0xff;		bt848->ehactivelo = bt848->ohactivelo = ntsc_hactive & 0xff;		bt848->ehdelaylo = bt848->ohdelaylo = hdelay & 0xff;		bt848->evactivelo = bt848->ovactivelo = ntsc_vactive & 0xff;		bt848->evdelaylo = bt848->ovdelaylo = ntsc_vdelay & 0xff;		bt848->ecrop = bt848->ocrop = 			((ntsc_hactive >> 8) & 0x03) |			((hdelay >> 6) & 0x0C) |	        		((ntsc_vactive >> 4) & 0x30) |			((ntsc_vdelay >> 2) & 0xC0);			bt848->colorctl = colorctl_gamma;		bt848->capctl = 0;		bt848->gpiodmactl = gpiodmactl_pltp23_16 |			gpiodmactl_pltp1_16 | gpiodmactl_pktp_32;		bt848->gpioreginp = 0;		bt848->contrastlo = contrast_100percent;		bt848->bright = 16;		bt848->adc = (2 << 6) | adc_crush;		bt848->econtrol = bt848->ocontrol = control_ldec;		bt848->escloop = bt848->oscloop = 0;		bt848->intstat = (ulong)-1;		bt848->intmask = intmask | 			intstat_vsync | intstat_scerr | intstat_risci | intstat_ocerr | 			intstat_vpress | intstat_fmtchg;		if (tv->amux) {			gpioenable(tv, ~0xfff, 0xfff);			gpiowrite(tv, ~0xfff, tv->amux[AudioRadio]);		}		print("#V%ld: %s (rev %d) (%s/%s) intl %d\n", 				tv - tvs, tv->variant->name, pci->rid, boards[tv->board],				tv->tuner->name, pci->intl);		intrenable(pci->intl, (void (*)(Ureg *, void *))tvinterrupt, 				tv, pci->tbdf, "tv");		}}static Chan*tvattach(char *spec){	return devattach('V', spec);}#define TYPE(q)		((int)((q).path & 0xff))#define DEV(q)			((int)(((q).path >> 8) & 0xff))#define QID(d, t)		((((d) & 0xff) << 8) | (t))static inttv1gen(Chan *c, int i, Dir *dp){	Qid qid;	switch (i) {	case Qvdata:		mkqid(&qid, QID(DEV(c->qid), Qvdata), 0, QTFILE);		devdir(c, qid, "video", 0, eve, 0444, dp);		return 1;	case Qadata:		mkqid(&qid, QID(DEV(c->qid), Qadata), 0, QTFILE);		devdir(c, qid, "audio", 0, eve, 0444, dp);		return 1;	case Qctl:		mkqid(&qid, QID(DEV(c->qid), Qctl), 0, QTFILE);		devdir(c, qid, "ctl", 0, eve, 0444, dp);		return 1;	case Qregs:		mkqid(&qid, QID(DEV(c->qid), Qregs), 0, QTFILE);		devdir(c, qid, "regs", 0, eve, 0444, dp);		return 1;	}	return -1;}static inttvgen(Chan *c, char *, Dirtab *, int, int i, Dir *dp){	Qid qid;	int dev;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -