Monitor *dirtomon(enum wlr_direction dir) {
	struct wlr_output *next;
	if (!wlr_output_layout_get(output_layout, selmon->wlr_output))
		return selmon;
	if ((next = wlr_output_layout_adjacent_output(output_layout, 1 << dir,
												  selmon->wlr_output,
												  selmon->m.x, selmon->m.y)))
		return next->data;
	if ((next = wlr_output_layout_farthest_output(
			 output_layout,
			 dir ^ (WLR_DIRECTION_LEFT | WLR_DIRECTION_RIGHT |
					WLR_DIRECTION_UP | WLR_DIRECTION_DOWN),
			 selmon->wlr_output, selmon->m.x, selmon->m.y)))
		return next->data;
	return selmon;
}

bool is_scroller_layout(Monitor *m) {

	if (m->pertag->ltidxs[m->pertag->curtag]->id == SCROLLER)
		return true;

	if (m->pertag->ltidxs[m->pertag->curtag]->id == VERTICAL_SCROLLER)
		return true;

	return false;
}

bool is_centertile_layout(Monitor *m) {

	if (m->pertag->ltidxs[m->pertag->curtag]->id == CENTER_TILE)
		return true;

	return false;
}

uint32_t get_tag_status(uint32_t tag, Monitor *m) {
	Client *c = NULL;
	uint32_t status = 0;
	wl_list_for_each(c, &clients, link) {
		if (c->mon == m && c->tags & 1 << (tag - 1) & TAGMASK) {
			if (c->isurgent) {
				status = 2;
				break;
			}
			status = 1;
		}
	}
	return status;
}

uint32_t get_tags_first_tag_num(uint32_t source_tags) {
	uint32_t i, tag;
	tag = 0;

	if (!source_tags) {
		return 0;
	}

	for (i = 0; !(tag & 1) && source_tags != 0 && i < LENGTH(tags); i++) {
		tag = source_tags >> i;
	}

	if (i == 1) {
		return 1;
	} else if (i > 9) {
		return 9;
	} else {
		return i;
	}
}

// 获取tags中最前面的tag的tagmask
uint32_t get_tags_first_tag(uint32_t source_tags) {
	uint32_t i, tag;
	tag = 0;

	if (!source_tags) {
		return selmon->pertag->curtag;
	}

	for (i = 0; !(tag & 1) && source_tags != 0 && i < LENGTH(tags); i++) {
		tag = source_tags >> i;
	}

	if (i == 1) {
		return 1;
	} else if (i > 9) {
		return 1 << 8;
	} else {
		return 1 << (i - 1);
	}
}

Monitor *xytomon(double x, double y) {
	struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
	return o ? o->data : NULL;
}

Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly) {
	double closest_x, closest_y;
	wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x,
									&closest_y);

	return output_from_wlr_output(
		wlr_output_layout_output_at(output_layout, closest_x, closest_y));
}

bool match_monitor_spec(char *spec, Monitor *m) {
	if (!spec || !m)
		return false;

	// if the spec does not contain a colon, treat it as a match on the monitor
	// name
	if (strchr(spec, ':') == NULL) {
		return regex_match(spec, m->wlr_output->name);
	}

	char *spec_copy = strdup(spec);
	if (!spec_copy)
		return false;

	char *name_rule = NULL;
	char *make_rule = NULL;
	char *model_rule = NULL;
	char *serial_rule = NULL;

	char *token = strtok(spec_copy, "&&");
	while (token) {
		char *colon = strchr(token, ':');
		if (colon) {
			*colon = '\0';
			char *key = token;
			char *value = colon + 1;

			if (strcmp(key, "name") == 0)
				name_rule = strdup(value);
			else if (strcmp(key, "make") == 0)
				make_rule = strdup(value);
			else if (strcmp(key, "model") == 0)
				model_rule = strdup(value);
			else if (strcmp(key, "serial") == 0)
				serial_rule = strdup(value);
		}
		token = strtok(NULL, "&&");
	}

	bool match = true;

	if (name_rule) {
		if (!regex_match(name_rule, m->wlr_output->name))
			match = false;
	}
	if (make_rule) {
		if (!m->wlr_output->make || strcmp(make_rule, m->wlr_output->make) != 0)
			match = false;
	}
	if (model_rule) {
		if (!m->wlr_output->model ||
			strcmp(model_rule, m->wlr_output->model) != 0)
			match = false;
	}
	if (serial_rule) {
		if (!m->wlr_output->serial ||
			strcmp(serial_rule, m->wlr_output->serial) != 0)
			match = false;
	}

	free(spec_copy);
	free(name_rule);
	free(make_rule);
	free(model_rule);
	free(serial_rule);

	return match;
}